JavaRush /Java Blog /Random EN /We are adding the ability to subscribe to a group of arti...

We are adding the ability to subscribe to a group of articles. (Part 3) - "Java project from A to Z"

Published in the Random EN group
Hi again. This is the final article from STEP_6, in which we will talk about adding functionality to the JRTB-6 task . In those two previous articles ( part 1 , part 2 ) we have already prepared almost everything you need. This part is the culmination of the process. To everyone who has read this series of articles up to this point from the very beginning - great respect. This means that you have enough motivation to find a great job. Now let's get down to business."Java project from A to Z": Adding the ability to subscribe to a group of articles.  Part 3 - 1

We implement JRTB-6

This time we will do the task from the side of the telegram bot, because the work on updating the database is all done, the database entities are configured and ready for work. Let's add a new value to CommandName - LIST_GROUP_SUB:
LIST_GROUP_SUB("/listGroupSub");
Let's create a ListGroupSubCommand command :
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;

import javax.ws.rs.NotFoundException;
import java.util.stream.Collectors;

import static com.github.javarushcommunity.jrtb.command.CommandUtils.getChatId;

/**
* {@link Command} for getting list of {@link GroupSub}.
*/
public class ListGroupSubCommand implements Command {

   private final SendBotMessageService sendBotMessageService;
   private final TelegramUserService telegramUserService;

   public ListGroupSubCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
       this.sendBotMessageService = sendBotMessageService;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public void execute(Update update) {
       //todo add exception handling
       TelegramUser telegramUser = telegramUserService.findByChatId(getChatId(update))
               .orElseThrow(NotFoundException::new);

       String message = "Я нашел все подписки на группы: \n\n";
       String collectedGroups = telegramUser.getGroupSubs().stream()
               .map(it -> "Группа: " + it.getTitle() + " , ID = " + it.getId() + " \n")
               .collect(Collectors.joining());

       sendBotMessageService.sendMessage(telegramUser.getChatId(), message + collectedGroups);
   }
}
Here everything is as simple as possible - we get the user using the existing chat_id, and all his subscriptions to groups will be collected in his object. We set this up in the second part. Again, I added //todo so that I wouldn’t forget to add handling of exceptions that may arise during operation. The next step is to update the CommandContainer by adding a new command to it:
put(LIST_GROUP_SUB.getCommandName(), new GroupSubListCommand(sendBotMessageService, telegramUserService))
That’s basically it: now you need to write more tests, update the /help command (add a description for new commands) and test the new functionality via Telegram. Let's write a test for ListGroupSubCommand . Since the logic of the command is not typical, we will write a test without being tied to the AbstractCommandTest class, as we did before:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.github.javarushcommunity.jrtb.command.CommandName.LIST_GROUP_SUB;

@DisplayName("Unit-level testing for ListGroupSubCommand")
public class ListGroupSubCommandTest {

   @Test
   public void shouldProperlyShowsListGroupSub() {
       //given
       TelegramUser telegramUser = new TelegramUser();
       telegramUser.setActive(true);
       telegramUser.setChatId("1");

       List<GroupSub> groupSubList = new ArrayList<>();
       groupSubList.add(populateGroupSub(1, "gs1"));
       groupSubList.add(populateGroupSub(2, "gs2"));
       groupSubList.add(populateGroupSub(3, "gs3"));
       groupSubList.add(populateGroupSub(4, "gs4"));

       telegramUser.setGroupSubs(groupSubList);

       SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
       TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);

       Mockito.when(telegramUserService.findByChatId(telegramUser.getChatId())).thenReturn(Optional.of(telegramUser));

       ListGroupSubCommand command = new ListGroupSubCommand(sendBotMessageService, telegramUserService);

       Update update = new Update();
       Message message = Mockito.mock(Message.class);
       Mockito.when(message.getChatId()).thenReturn(Long.valueOf(telegramUser.getChatId()));
       Mockito.when(message.getText()).thenReturn(LIST_GROUP_SUB.getCommandName());
       update.setMessage(message);

       String collectedGroups = "Я нашел все подписки на группы: \n\n" +
               telegramUser.getGroupSubs().stream()
                       .map(it -> "Группа: " + it.getTitle() + " , ID = " + it.getId() + " \n")
                       .collect(Collectors.joining());

       //when
       command.execute(update);

       //then
       Mockito.verify(sendBotMessageService).sendMessage(telegramUser.getChatId(), collectedGroups);
   }

   private GroupSub populateGroupSub(Integer id, String title) {
       GroupSub gs = new GroupSub();
       gs.setId(id);
       gs.setTitle(title);
       return gs;
   }
}

Let's update the /help command

In our case, the /help command acts as documentation for working with the bot, so we must remember to update it so that the user can use it. We have added two commands, so let's update the text that will come:
public static final String HELP_MESSAGE = String.format("✨Дотупные команды✨\n\n"

               + "Начать\\закончить работу с ботом:\n"
               + "%s - начать работу со мной\n"
               + "%s - приостановить работу со мной\n\n"

               + "Работа с подписками на группы:\n"
               + "%s - подписаться на группу статей\n"
               + "%s - получить список групп, на которые подписан\n\n"

               + "%s - получить помощь в работе со мной\n"
               + "%s - получить мою статистику использования\n",
       START.getCommandName(), STOP.getCommandName(), ADD_GROUP_SUB.getCommandName(),
       LIST_GROUP_SUB.getCommandName(), HELP.getCommandName(), STAT.getCommandName());
I also updated the text of the bot’s responses: I made it so that it was always on first name terms with the user, otherwise there would be both “you” and “you”... Now it will be possible to create at least some kind of connection in the bot’s work.

Testing the updated bot

We launch our bot locally and do the following:
  1. We execute the /start command to be sure that the user in the test case has been added to the database.
  2. We execute the /help command - we check that everything is ok, as we wanted.
  3. Next we execute the /addGroupSub command.
  4. From the proposed list of group IDs, we add several to the mix.
  5. We run the /listGroupSub command to make sure that the groups are registered to the user.
Go! We launch the database via docker-compose-test.yml and start our SpringBoot. Next, go to our test bot and execute the /start command, followed by /help : "Java project from A to Z": Adding the ability to subscribe to a group of articles.  Part 3 - 2Next, enter the command /addGroupSub : "Java project from A to Z": Adding the ability to subscribe to a group of articles.  Part 3 - 3The drop-down list says that the Java client is working as it should: we have all the groups with their IDs, a description of the command helps (hopefully) understand what we need next, so we add several groups to the subscription: "Java project from A to Z": Adding the ability to subscribe to a group of articles.  Part 3 - 4Now we have 5 subscriptions, so we can run the /listGroupSub command : And then we get some kind of crazy thing... It’s not clear why the title"Java project from A to Z": Adding the ability to subscribe to a group of articles.  Part 3 - 5 was just showing without any problems, but not here. Let's go to the database to see what's there: The same questions are recorded in the database, but only for those with Cyrillic alphabet. This means there is some problem with the encoding. Apparently, you need to configure the database and the driver for connecting to the database. We need UTF-8. But how to add it? After several minutes of searching on the Internet, I found : the driver needs to update the url variable. And for setting up a docker image in docker-compose, the very first link , but the answer is not the first)) Therefore, knowing this, let’s update the properties and docker-compose files. "Java project from A to Z": Adding the ability to subscribe to a group of articles.  Part 3 - 6
application.properties:
spring.datasource.url=jdbc:mysql://jrtb-db:3306/jrtb_db?characterEncoding=UTF-8

application-test.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/dev_jrtb_db?characterEncoding=UTF-8

docker-compose.yml (добавил последнюю строку):
jrtb-db:
 image: mysql:5.7
 restart: always
 environment:
   MYSQL_USER: ${BOT_DB_USERNAME}
   MYSQL_PASSWORD: ${BOT_DB_PASSWORD}
   MYSQL_DATABASE: 'jrtb_db'
   MYSQL_ROOT_PASSWORD: 'root'
 ports:
   - '3306:3306'
 expose:
   - '3306'
 command: --character-set-server=utf8 --collation-server=utf8_general_ci

docker-compose-test.yml (добавил последнюю строку)
jrtb-db-dev:
 image: mysql:5.7
 restart: always
 environment:
   MYSQL_DATABASE: 'dev_jrtb_db'
   # So you don't have to use root, but you can if you like
   MYSQL_USER: 'dev_jrtb_db_user'
   # You can use whatever password you like
   MYSQL_PASSWORD: 'dev_jrtb_db_password'
   # Password for root access
   MYSQL_ROOT_PASSWORD: 'root'
 ports:
   # <Port exposed> : < MySQL Port running inside container>
   - '3306:3306'
 expose:
   # Opens port 3306 on the container
     - '3306'
 command: --character-set-server=utf8 --collation-server=utf8_general_ci
After these updates, you need to erase all data in the database and start over. Erasing is very simple: you need to run the command: docker-compose -f docker-compose-test.yml down after which all data and database are deleted. And run it again, with the updated encoding: docker-compose -f docker-compose-test.uml up The database is ready. Let's launch the updated application and take a look. I’ll go over it quickly and show you the result: "Java project from A to Z": Adding the ability to subscribe to a group of articles.  Part 3 - 7And now we got exactly what we wanted. Now this looks like the truth.

Ending

Now I think we can complete this step. A lot has been done, really a lot. Let's update the application version to 0.5.0-SNAPSHOT and RELEASE_NOTES.
# Release Notes ## 0.5.0-SNAPSHOT * JRTB-5: added ability to subscribe on group * JRTB-6: added ability to get a list of group subscriptions.
Then everything is as usual: we create a new commit with all the changes. The main thing is to add a description of the two tasks that were completed during this step for reporting purposes. So here's the comment:
STEP_6 JRTB-5: added ability to subscribe on group JRTB-6: added ability to see the list of the group subscription.
This resulted in 47 changed files... That's a big change. Although you can’t tell from the description of functionality. After all, to understand the full depth, you need to know that you need to write a Java client for the API, essentially updating the entire application. This is how it is, working on a server - there is a lot of work, but visibility from the client side is small...)) Friends, I traditionally offer you a way to show interest in my work - subscribe to a github account , join the telegram channel and write a question about the article, if something is not clear! Here is a link to the pull request with changes for this STEP_6 . Thanks everyone for reading. More to come - let's talk about deleting a subscription, deactivating a profile, and more. Don't switch))

A list of all materials in the series is at the beginning of this article.

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION