JavaRush/Java блог/Random UA/Додаємо можливість передплатити групу статей. (Частина 3)...
Roman Beekeeper
35 рівень

Додаємо можливість передплатити групу статей. (Частина 3) - "Java-проект від А до Я"

Стаття з групи Random UA
учасників
Ще раз привіт. Це заключна стаття STEP_6, в якій ми поговоримо про додавання функціональності завдання JRTB-6 . У тих двох попередніх статтях ( частина 1 , частина 2 ) ми вже підготували майже все, що потрібно. У цій частині – кульмінація процесу. Усім, хто дочитав цю серію статей до цього моменту від самого початку, — великий респект. Це означає, що мотивації вистачить на те, щоб знайти чудову роботу. А тепер перейдемо до діла."Java-проект від А до Я": Додаємо можливість передплатити групу статей.  Частина 3 - 1

Реалізуємо 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());
Також я оновив текст відповідей бота: зробив так, щоб він завжди був на "ти" з користувачем, а то там було і "ти", і "ви"... Тепер можна буде створити зв'язок у роботі бота.

Тестуємо роботу оновленого робота

Локально запускаємо нашого бота і робимо таке:
  1. Виконуємо /start команду – щоб бути впевненим, що користувач у тестовому випадку доданий до БД.
  2. Виконуємо /help команду - перевіряємо, що все, як ми хотіли.
  3. Далі виконуємо команду /addGroupSub .
  4. Із запропонованого списку ID-шників груп додаємо кілька разнобій.
  5. Виконуємо команду /listGroupSub , щоб переконатися, що групи записані на користувача.
Поїхали! Запускаємо БД через docker-compose-test.yml та стартуємо наш SpringBoot. Далі, заходимо в нашого тестового бота і виконуємо /start команду, а за нею - /help : "Java-проект від А до Я": Додаємо можливість передплатити групу статей.  Частина 3 - 2Далі вводимо команду /addGroupSub : "Java-проект від А до Я": Додаємо можливість передплатити групу статей.  Частина 3 - 3Випадаючий список говорить, що Java-клієнт працює як потрібно: у нас всі групи з їх ID-шниками, опис команди допомагає (сподіваюся) зрозуміти, що потрібно далі, тому додаємо кілька груп у підписку: "Java-проект від А до Я": Додаємо можливість передплатити групу статей.  Частина 3 - 4Тепер у нас є 5 підписок, так що можна виконати команду /listGroupSub : "Java-проект від А до Я": Додаємо можливість передплатити групу статей.  Частина 3 – 5І тут ми отримуємо якусь дичину... Незрозуміло, чому щойно показувало title без будь-яких проблем, а тут ні. Ідемо до бази даних, щоб подивитися що там:"Java-проект від А до Я": Додаємо можливість передплатити групу статей.  Частина 3 - 6У базі даних записані самі питання, але тільки для тих, де кирабоця. Значить якась проблема з кодуванням. Потрібно налаштувати, мабуть, базу даних та драйвер для з'єднання з БД. Нам потрібна UTF-8. Але як його додати? Після кількох хвабон пошуку в інтернетах знайшов : для драйвера потрібно оновити url змінну. І для налаштування образу докера в docker-compose взагалі найперше посилання , але відповідь не перша)) Тому, знаючи це, оновимо проперти і docker-compose файли.
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 База готова. Запускаємо оновлений додаток і дивимося. Я одразу пробігу швидко і покажу результат: "Java-проект від А до Я": Додаємо можливість передплатити групу статей.  Частина 3 - 7І ось тепер ми отримали саме те, що й хотіли. Ось це схоже на правду.

Закінчення

Тепер я думаю, що можна завершувати роботу з цього кроку. Зроблено багато, справді багато. Оновимо версію програми до 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 . Дякую всім за прочитання. Далі більше — поговоримо про видалення передплати, деактивацію профілю та інше. Не перемикайтесь))

Список всіх матеріалів серії на початку цієї статті.

Коментарі
  • популярні
  • нові
  • старі
Щоб залишити коментар, потрібно ввійти в систему
Для цієї сторінки немає коментарів.