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.
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:- We execute the /start command to be sure that the user in the test case has been added to the database.
- We execute the /help command - we check that everything is ok, as we wanted.
- Next we execute the /addGroupSub command.
- From the proposed list of group IDs, we add several to the mix.
- We run the /listGroupSub command to make sure that the groups are registered to the user.
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: And 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))
GO TO FULL VERSION