Let's refactor our code
Let's create a new branch for this case: STEP_10, in which we will write down all the latest changes before the MVP. Refactoring is changing the codebase without changing its behavior. What do you want to change? At the very beginning, I chose the word article as the term “article” on the project, and until I started working with the Javarush API client for articles, it was logical. But the Javarush API uses the term post . And I think it would be good to switch to their standard, so the first thing we do is rename everything from article to post. What will be our guarantee that everything remains the same? The guarantee will be our tests that we have written. Now we need them. Let's start with the database. In the group_sub table, you need to rename the lastArticleId field to lastPostId. This is very simple because we already have Flyway configured and in order to update the field, we just need to add one more migration in which we will perform an ALTER TABLE operation. Let's create a new, third, migration: V00003__rename_last_article_id.sql which will be:ALTER TABLE group_sub CHANGE COLUMN last_article_id last_post_id INT;
This migration needs to be placed in the same place as the previous two. Next, you need to rename the variable name in the GroupSub entity from lastArticleId to lastPostId. If the work goes through IDEA, then renaming is very simple, you need to use the hot key: shift + F6, when the cursor is on the class / method / field name, then the development environment allows you to rename all the places where this field is used. It will also rename the getters and setters for this field. This is a useful item and should be in every developer's arsenal. I'm sure that the eclipse has the same functionality, but I can't tell you, I don't use it. so rename
@Column(name = "last_article_id")
private Integer lastArticleId;
V
@Column(name = "last_post_id")
private Integer lastPostId;
Next, using the search throughout the project (ctrl + shift + f), we will find all the places where there are mentions of Article: It can be seen from the figure that we still have class and method names with the word Article. They also need to be renamed to Post. We use the already known command shift + F6 for them:
-
class FindNewArticlesJob -> FindNewPostsJob
-
class FindNewArticleService -> FindNewPostsService
-
method FindNewArticleService#findNewArticles -> FindNewPostsService#findNewPosts
-
class FindNewArticleServiceImpl -> FindNewPostsServiceImpl
-
property bot.recountNewArticleFixedRate -> bot.recountNewPostFixedRate
-
method CodeGymGroupClient#findLastArticleId -> CodeGymGroupClient#findLastPostId
-
method FindNewPostsJob#findNewArticles -> FindNewPostsJob#findNewPosts
-
And other places where internal methods, where comments.
- method CommandContainer#retrieveCommand -> CommandContainer#findCommand
- all javadocs with retrieve in find .
/**
* Service for sending messages via telegram-bot.
*/
public interface SendBotMessageService {
/**
* Send message via telegram bot.
*
* @param chatId provided chatId in which would be sent.
* @param message provided message to be sent.
*/
void sendMessage(Long chatId, String message);
/**
* Send messages via telegram bot.
*
* @param chatId provided chatId in which would be sent.
* @param message collection of provided messages to be sent.
*/
void sendMessage(Long chatId, List<String> message);
}
Now the interface accepts not String chatId , but Long chatId . We also update the implementation, just put chatId.toString() in the required field:
sendMessage.setChatId(chatId.toString());
Next, go to the TelegramUser class and change the type of the chatId variable from String to Long :
@Id
@Column(name = "chat_id")
private Long chatId;
In TelegramUserRepostiry we also change from String to Long in the inherited interface :
/**
* {@link Repository} for handling with {@link TelegramUser} entity.
*/
@Repository
public interface TelegramUserRepository extends JpaRepository<TelegramUser, Long> {
List<TelegramUser> findAllByActiveTrue();
List<TelegramUser> findAllByActiveFalse();
}
Update the CommandUtils#getChatId method to not result in a string:
/**
* Get chatId from {@link Update} object.
*
* @param update provided {@link Update}
* @return chatID from the provided {@link Update} object.
*/
public static Long getChatId(Update update) {
return update.getMessage().getChatId();
}
And in all places where chatId is used , we remove the cast to the string. Where methods accept String chatId , change the type to Long . By the way, while changing the cast to the string, I found that the getChatId(update) utility method is not used everywhere, so I updated those places as well. The last step is to update the field type in the tg_user table . To do this, let's create the fourth migration V00004_change_chat_id_type_to_long.sql :
ALTER TABLE tg_user MODIFY chat_id INT;
After all these updates, it's time to run the tests :D As a result, only one test fell, it could have been worse. It turned out that the test crashed due to the fact that JUnit allows compiling a comparison of different types during assert: As soon as I did Long.valueOf (i + 1) instead of String.valueOf( i + 1) , the test started working as expected. Now we need to make sure that the bot starts and works as expected. I'll try to add a subscription, see the list of subscriptions and delete one of them. I think this is enough to check for a smoke test. Poklatsal everything I could, everything works. This is good, so the refactoring went exactly as I wanted - the code changed, but the changes did not affect the behavior of the bot in any way. We did a good refactoring of the project. The terms were brought to a common denominator and the confusion with the type of the chatId variable was removed .
Final preparations before the release of the stable version
Now that all the code that was planned has already been written, we can make the final preparations. Let's update the version in the application to stable 1.0.0 :<version>1.0.0</version>
and add a description to RELEASE_NOTES:
Building our first release: v1.0
All the code that we planned to fit into the MVP is ready, and now we need to draw a line - create a release. What is a release? This is again a tracing paper from the English word release, which in our case means the release of a new version of the product. To do this, GitHub has a Releases section in the repository , in which releases will be stored. They just need to be created. Let's merge the latest pull request and create our first release based on the latest commit in the main branch. Let's go and create a new release and fill it with data: Everything, you can click Publish Release. And that's it, now we have our first release of the application. It's time for a retrospective.What's next for the bot?
Of course, I want the bot to start developing at the expense of the community. For young developers to offer their ideas for implementing new functionality. But you won’t be forced to be nice here, as long as there are no such people, and I will develop it to the best of my ability. There are several areas that are of interest to me:-
Create a job to collect and save bot statistics on a daily basis so that you can track its development over time.
-
Set up work with backups, in general, the idea is described in the previous article.
-
Think about unitarizing the approach in creating a telegram bot to receive notifications of new articles. Ideally (although I don’t know if this will work) to make it so that there is a published Spring boot starter that would require setting up a database, a bot, and implementing work interfaces to get data about articles, and this library would do the rest. Let's see what can come of this, we need to think carefully here.
-
Expand statistics for all by authors: how many articles, number of views of articles, what is the rating of articles. For obvious reasons, CodeGym is not a blog for writing articles, and therefore it simply does not have such functionality. And for me, as an author, this is interesting. With the help of a bot, you can create thoughtful statistics and maintain them.
-
Implement exception handling in a way that makes it clear to the user what happened.
-
Separate Java library with CodeGym API client.
-
Add a GitHub action that automatically creates a release each time the main branch is updated.
-
Add a subscription to a specific author, not a group of articles.
-
Improve the UI presentation of the bot. Instead of ugly commands, create beautiful buttons that would help to control the bot in a more pleasant and elegant way.
-
Since information about new articles may be of interest to some other channels (at least I have such interest in my articles and my telegram channel), it would be nice to add the ability for the bot to send messages about new articles directly to some channel, not just private messages.
-
Study the experience of bots that work in groups and see what can be done with this in our case. I'm sure there are closed groups of Java learners who would be interested in being notified of new articles.
Retrospective (instead of ending)
I've been repeating this word a lot over the past few articles. What does it mean? For me, within the framework of this series, it is to look at what I wanted to do and what happened during these 8 months. There were many comments on the topic that all this is cool, even if the author did it to the end: Now we can already say that yes, I did finish it. Did I think it would be sooo long? No, I didn't. I expected to do before the beginning of 2021. But I realized that I need to talk about databases, which went to a whole series of articles, and about many technologies. And it stretched out. Although I can say that I am satisfied with the result and the volume of work. I see people who learn from it, express their gratitude, and this motivates me to go further. What did it give me? First of all, understanding that such projects can be taken on and they are of interest. I always read comments on articles with great pleasure and respond to them to the best of my ability. Of course, I became more experienced in both writing articles and writing applications, because the process of deploying the application required me to improve my skills. Yes, I knew what I wanted to do in the end, I knew that it was real. And it still took time to study the implementation of my ideas. In the process of writing this series, I wanted to somehow organize my materials in one place, and for this I created my owntelegram channel , which already has more than 400 subscribers. For me, this is a great result. New direction of development. Now there is an understanding of where to grow further. Also in the process, a new product was born that people already use, and I in particular - Javarush Telegram Bot . Yes, so far only 26 active users. But I believe that it will be in demand and the number of users will grow. What is good about this project? It is still to be developed and developed. Students from CodeGym who want to hone their skills would be able to propose new features and implement them. And I will lead this project and conduct a Code Review of all changes. We finalize what was planned and what happened:-
The main goal of the project was to write a database application that would be configured to work on deployment and management. This goal has been definitely achieved.
-
The development deadlines shifted and there were problems with the release of articles. There were moments when there were no articles for 2-3 weeks.
-
Completely set up work with the database, added Flyway.
-
Described working with Maven in several articles.
-
We talked about Docker. Not as much and detailed as we would like, but still.
-
We also touched on bash scripts, we have set up the launch of the entire deployment on this.
-
According to the planning of the project, we can say that everything went as realistically as possible. Only a few tasks were added during the writing process.
-
We did not fully consider the comparison of two solutions - Flyway and Liquibase.
-
Somehow we casually talked about Lombok'e. So only in practice and all. I would like more.
-
Little time was spent on the UniRest solution.
GO TO FULL VERSION