Ще раз привіт. Це заключна стаття STEP_6, в якій ми поговоримо про додавання функціональності завдання JRTB-6 . У тих двох попередніх статтях ( частина 1 , частина 2 ) ми вже підготували майже все, що потрібно. У цій частині – кульмінація процесу. Усім, хто дочитав цю серію статей до цього моменту від самого початку, — великий респект. Це означає, що мотивації вистачить на те, щоб знайти чудову роботу. А тепер перейдемо до діла.
Реалізуємо JRTB-6
Цього разу робитимемо завдання з боку телеграм-бота, тому що робота з оновлення БД вся зроблена, сутності БД налаштовані та готові до роботи. Додамо в CommandName нове значення - LIST_GROUP_SUB:LIST_GROUP_SUB("/listGroupSub");
Створимо команду ListGroupSubCommand :
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);
}
}
Тут все максимально просто - за наявним chat_id отримуємо користувача, і в нього вже в об'єкті будуть зібрані всі його підписки на групи. Ми це налаштовували у другій частині. Знову ж таки, додав //todo, щоб не забути додати обробку винятків, які можуть з'явитися під час роботи. Наступним етапом оновлюємо CommandContainer , додаючи йому нову команду:
put(LIST_GROUP_SUB.getCommandName(), new GroupSubListCommand(sendBotMessageService, telegramUserService))
По суті все: тепер потрібно написати ще тести, оновити команду /help (додати опис для нових команд) і протестувати новий функціонал через Телеграм. Напишемо тест для ListGroupSubCommand . Так як логіка у команди не типова, то писатимемо тест, не прив'язуючись до AbstractCommandTest класу, як ми робабо до цього:
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;
}
}
Обновимо /help команду
У нашому випадку /help команда виконує роль документації для роботи з ботом, тому її потрібно не забувати оновлювати, щоб користувач зміг нею скористатися. У нас додалися дві команди, тому оновимо текст, який приходитиме: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());
Також я оновив текст відповідей бота: зробив так, щоб він завжди був на "ти" з користувачем, а то там було і "ти", і "ви"... Тепер можна буде створити зв'язок у роботі бота.
Тестуємо роботу оновленого робота
Локально запускаємо нашого бота і робимо таке:- Виконуємо /start команду – щоб бути впевненим, що користувач у тестовому випадку доданий до БД.
- Виконуємо /help команду - перевіряємо, що все, як ми хотіли.
- Далі виконуємо команду /addGroupSub .
- Із запропонованого списку ID-шників груп додаємо кілька разнобій.
- Виконуємо команду /listGroupSub , щоб переконатися, що групи записані на користувача.
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
Після цих оновлень потрібно стерти усі дані в БД та почати заново. Стерти дуже просто: потрібно виконати команду: docker-compose -f docker-compose-test.yml down після чого всі дані та БД видаляються. І знову запустити, вже з оновленим кодуванням: docker-compose -f docker-compose-test.uml up База готова. Запускаємо оновлений додаток і дивимося. Я одразу пробігу швидко і покажу результат: І ось тепер ми отримали саме те, що й хотіли. Ось це схоже на правду.
Закінчення
Тепер я думаю, що можна завершувати роботу з цього кроку. Зроблено багато, справді багато. Оновимо версію програми до 0.5.0-SNAPSHOT та RELEASE_NOTES.
# Release Notes ## 0.5.0-SNAPSHOT * JRTB-5: added ability subscribe on group * JRTB-6: added ability to get a list of group subscriptions.
Далі все як завжди: створюємо новий коміт із усіма змінами. Головне — для звітності додати опис двох завдань, які зробабо цей крок. Тому такий буде комент:
STEP_6 JRTB-5: розширена здатність до підпису на групі JRTB-6: розширена здатність до сторінки текстової групи.
У результаті вийшло 47 змінених файлів. Це велика зміна. Хоча за описом функціональності й не скажеш. Адже щоб зрозуміти всю глибину, потрібно знати, що необхідно написати джава клієнт до АПІ, оновити всі програми по суті. Ось така вона, робота на сервері - роботи багато, а видимість з боку клієнтської частини невелика ...)) Друзі, традиційно пропоную вам спосіб показати інтерес до моєї роботи - підписатися на аккаунт гітхаб , приєднатися до телеграм-каналу і написати питання по статті, якщо щось незрозуміло! Ось посилання на пулл-реквест із змінами за цей STEP_6 . Дякую всім за прочитання. Далі більше — поговоримо про видалення передплати, деактивацію профілю та інше. Не перемикайтесь))
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ