Hi again. This is the final article from STEP_6 where we will talk about adding JRTB-6 task functionality . In those two previous articles ( part 1 , part 2 ) we have already prepared almost everything that is needed. 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 the motivation is enough to find a great job. And now let's get down to business.
Implementing JRTB-6
This time we will do the task on the part of the telegram bot, because the work on updating the database is all done, the database entities are configured and ready to go. Let's add a new value to CommandName - LIST_GROUP_SUB:LIST_GROUP_SUB("/listGroupSub");
Let's create a ListGroupSubCommand command :
package com.github.codegymcommunity.jrtb.command;
import com.github.codegymcommunity.jrtb.repository.entity.GroupSub;
import com.github.codegymcommunity.jrtb.repository.entity.TelegramUser;
import com.github.codegymcommunity.jrtb.service.SendBotMessageService;
import com.github.codegymcommunity.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.codegymcommunity.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 from the existing chat_id, and all his subscriptions to groups will be collected in the object. We set it up in the second part. Again, I added //todo so as not to forget to add exception handling that may appear 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))
In fact, everything: now you need to write more tests, update the /help command (add a description for new commands) and test the new functionality through 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.codegymcommunity.jrtb.command;
import com.github.codegymcommunity.jrtb.repository.entity.GroupSub;
import com.github.codegymcommunity.jrtb.repository.entity.TelegramUser;
import com.github.codegymcommunity.jrtb.service.SendBotMessageService;
import com.github.codegymcommunity.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.codegymcommunity.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;
}
}
Update /help command
In our case, the /help command acts as documentation for working with the bot, so you need to remember to update it so that the user can use it. We have added two commands, so we will 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 “you” with the user, otherwise there was both “you” and “you” ... Now it will be possible to create at least some connection in the work of the bot.
Testing the work of the updated bot
Run our bot locally and do the following:- We execute the /start command - to be sure that the user in the test case is added to the database.
- We execute the /help command - we check that everything is OK, as we wanted.
- Next, execute the /addGroupSub command.
- From the proposed list of group IDs, we add a few to the discord.
- Run the /listGroupSub command to make sure that the groups are written 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 the data in the database and start over. Deleting is very simple: you need to run the command: docker-compose -f docker-compose-test.yml down after which all data and the database are deleted. And run again, with the updated encoding: docker-compose -f docker-compose-test.uml up The database is ready. We launch the updated application and look. I'll run quickly and show the result: And now we got exactly what we wanted. This already looks like the truth.
Ending
Now I think that it is possible to complete the work on 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 done during this step for reporting. So here is the comment:
STEP_6 JRTB-5: added ability to subscribe on group JRTB-6: added ability to see the list of the group subscription.
As a result, 47 modified files came out ... This is a big change. Although you can't tell from the description of the functionality. After all, in order to understand the full depth, you need to know that you need to write a Java client for the API, update the entire application in essence. This is how it is, work on the server - there is a lot of work, and 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 a pull request with changes for this STEP_6 . Thank you all for reading. Further more - let's talk about deleting a subscription, deactivating a profile, and more. Don't switch))
GO TO FULL VERSION