JavaRush /Blog Java /Random-ES /Estamos agregando la posibilidad de suscribirse a un grup...

Estamos agregando la posibilidad de suscribirse a un grupo de artículos. (Parte 2) - "Proyecto Java de la A a la Z"

Publicado en el grupo Random-ES
¡Hola a todos! Seguimos trabajando en la tarea que iniciamos la semana pasada ."Proyecto Java de la A a la Z": Agregar la posibilidad de suscribirse a un grupo de artículos.  Parte 2 - 1

Implementamos JRTB-5

Ahora necesitamos agregar un comando para poder suscribirnos a algún grupo de artículos de JavaRush. ¿Cómo hacerlo? Seguiremos el escenario más simple que se me ocurrió. Como tenemos acceso por ID de grupo, necesitamos que el usuario lo transfiera. Para hacer esto, el usuario ingresará el comando /addGroupSub GROUP_ID, que funcionará de dos maneras: si solo viene el comando en sí: /addGroupSub , se envía en respuesta una lista de todos los grupos y sus ID. Luego, el usuario podrá seleccionar el ID de grupo que necesita y crear la segunda versión de la solicitud en este comando: /addGroupSub GROUP_ID - y luego habrá una entrada para este grupo con este usuario. Creo que podemos hacerlo mejor en el futuro. Nuestro objetivo es mostrar el desarrollo, y no la experiencia de usuario genial (me avergüenza decirlo, pero no sé el término en ruso que significaría esto). Para agregar correctamente una funcionalidad que abarque toda la aplicación (en nuestro caso, desde el cliente del bot de Telegram hasta la base de datos), debe comenzar por algún extremo. Haremos esto desde el lado de la base de datos.

Agregar una nueva migración a la base de datos

Lo primero que debe hacer es agregar una nueva migración de base de datos y la capacidad de guardar los datos de suscripción del grupo de usuarios en JR. Para recordar cómo debe ser, vuelve al artículo “ Planificación de proyectos: mide siete veces ”. Allí en la segunda foto hay un diagrama aproximado de la base de datos. Necesitamos agregar una tabla para almacenar información del grupo:
  • El ID del grupo en JavaRush también será nuestro ID. Confiamos en ellos y creemos que estas identificaciones son únicas;
  • título - en nuestras fotos era nombre - el nombre informal del grupo; es decir, lo que vemos en el sitio web de JavaRush;
  • last_article_id: y este es un campo interesante. Almacenará el último ID del artículo de este grupo, que el bot ya envió a sus suscriptores. Utilizando este campo funcionará el mecanismo de búsqueda de nuevos artículos. Los nuevos suscriptores no recibirán artículos publicados antes de que el usuario se suscribiera: sólo aquellos que fueron publicados después de suscribirse al grupo.
También tendremos una relación de muchos a muchos entre la tabla de grupos y usuarios, porque cada usuario puede tener muchas suscripciones de grupo (una a muchas) y cada suscripción de grupo puede tener muchos usuarios (una a muchas, solo Por otro lado). Resulta que este será nuestro muchos a muchos. Para aquellos que tengan preguntas, revisen los artículos en la base de datos. Sí, pronto planeo crear una publicación en el canal Telegram, donde reuniré todos los artículos de la base de datos. Así es como se verá nuestra segunda migración de base de datos.
V00002__created_groupsub_many_to_many.sql:

-- add PRIMARY KEY FOR tg_user
ALTER TABLE tg_user ADD PRIMARY KEY (chat_id);

-- ensure that the tables with these names are removed before creating a new one.
DROP TABLE IF EXISTS group_sub;
DROP TABLE IF EXISTS group_x_user;

CREATE TABLE group_sub (
   id INT,
   title VARCHAR(100),
   last_article_id INT,
   PRIMARY KEY (id)
);

CREATE TABLE group_x_user (
   group_sub_id INT NOT NULL,
   user_id VARCHAR(100) NOT NULL,
   FOREIGN KEY (user_id) REFERENCES tg_user(chat_id),
   FOREIGN KEY (group_sub_id) REFERENCES group_sub(id),
   UNIQUE(user_id, group_sub_id)
);
Es importante tener en cuenta que primero cambio la tabla anterior: le agrego una clave principal. De alguna manera me perdí esto en ese momento, pero ahora MySQL no me dio la oportunidad de agregar una CLAVE EXTRANJERA para la tabla gorup_x_user y, como parte de esta migración, actualicé la base de datos. Tenga en cuenta un aspecto importante. El cambio de la base de datos se debe hacer exactamente de esta manera: todo lo que se necesita está en la nueva migración, pero no actualizando una migración ya publicada. Sí, en nuestro caso no pasaría nada, ya que este es un proyecto de prueba y sabemos que está implementado en un solo lugar, pero este sería un enfoque equivocado. Pero queremos que todo esté bien. Luego viene eliminar tablas antes de crearlas. ¿Por qué es esto? De modo que si por casualidad hubiera tablas con esos nombres en la base de datos, la migración no fallaría y funcionaría exactamente como se esperaba. Y luego agregamos dos tablas. Todo fue como queríamos. Ahora necesitamos iniciar nuestra aplicación. Si todo comienza y no se rompe, entonces se registra la migración. Y para comprobarlo, vamos a la base de datos para asegurarnos de que: a) hayan aparecido dichas tablas; b) hay una nueva entrada en la tabla técnica de corredores aéreos. Esto completa el trabajo de migración, pasemos a los repositorios.

Agregar una capa de repositorio

Gracias a Spring Boot Data, aquí todo es muy simple: necesitamos agregar la entidad GroupSub, actualizar ligeramente TelegramUser y agregar un GroupSubRepository casi vacío: Agregamos la entidad GroupSub al mismo paquete que TelegramUser:
package com.github.javarushcommunity.jrtb.repository.entity;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.isNull;

@Data
@Entity
@Table(name = "group_sub")
@EqualsAndHashCode
public class GroupSub {

   @Id
   private Integer id;

   @Column(name = "title")
   private String title;

   @Column(name = "last_article_id")
   private Integer lastArticleId;

   @ManyToMany(fetch = FetchType.EAGER)
   @JoinTable(
           name = "group_x_user",
           joinColumns = @JoinColumn(name = "group_sub_id"),
           inverseJoinColumns = @JoinColumn(name = "user_id")
   )
   private List<TelegramUser> users;

   public void addUser(TelegramUser telegramUser) {
       if (isNull(users)) {
           users = new ArrayList<>();
       }
       users.add(telegramUser);
   }
}
Una cosa que vale la pena señalar es que tenemos un campo de usuarios adicional que contendrá una colección de todos los usuarios suscritos al grupo. Y dos anotaciones, ManyToMany y JoinTable, son exactamente lo que necesitamos para esto. Es necesario agregar el mismo campo para TelegramUser:
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
Este campo utiliza uniones escritas en la entidad GroupSub. Y, de hecho, nuestra clase de repositorio para GroupSub es GroupSubRepository :
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
* {@link Repository} for {@link GroupSub} entity.
*/
@Repository
public interface GroupSubRepository extends JpaRepository<GroupSub, Integer> {
}
En esta etapa, no necesitamos métodos adicionales: los implementados en el ancestro JpaRepository son suficientes para nosotros. Escribamos una prueba en TelegramUserRepositoryIT que comprobará que nuestro sistema de muchos a muchos funciona. La idea de la prueba es que agreguemos 5 grupos de suscripciones por usuario a la base de datos mediante un script sql, obtengamos este usuario por su ID y comprobemos que recibimos exactamente esos grupos y con exactamente los mismos valores. ¿Cómo hacerlo? Puede incrustar un contador en los datos, que luego podemos revisar y verificar. Aquí está el script fiveGroupSubsForUser.sql:
INSERT INTO tg_user VALUES (1, 1);

INSERT INTO group_sub VALUES
(1, 'g1', 1),
(2, 'g2', 2),
(3, 'g3', 3),
(4, 'g4', 4),
(5, 'g5', 5);

INSERT INTO group_x_user VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1);
Y la prueba en sí:
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/fiveGroupSubsForUser.sql"})
@Test
public void shouldProperlyGetAllGroupSubsForUser() {
   //when
   Optional<TelegramUser> userFromDB = telegramUserRepository.findById("1");

   //then
   Assertions.assertTrue(userFromDB.isPresent());
   List<GroupSub> groupSubs = userFromDB.get().getGroupSubs();
   for (int i = 0; i < groupSubs.size(); i++) {
       Assertions.assertEquals(String.format("g%s", (i + 1)), groupSubs.get(i).getTitle());
       Assertions.assertEquals(i + 1, groupSubs.get(i).getId());
       Assertions.assertEquals(i + 1, groupSubs.get(i).getLastArticleId());
   }
}
Ahora agreguemos una prueba del mismo significado para la entidad GroupSub. Para hacer esto, creemos una clase de prueba groupSubRepositoryIT en el mismo paquete que groupSubRepositoryIT :
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;

import java.util.List;
import java.util.Optional;

import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;

/**
* Integration-level testing for {@link GroupSubRepository}.
*/
@ActiveProfiles("test")
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class GroupSubRepositoryIT {

   @Autowired
   private GroupSubRepository groupSubRepository;

   @Sql(scripts = {"/sql/clearDbs.sql", "/sql/fiveUsersForGroupSub.sql"})
   @Test
   public void shouldProperlyGetAllUsersForGroupSub() {
       //when
       Optional<GroupSub> groupSubFromDB = groupSubRepository.findById(1);

       //then
       Assertions.assertTrue(groupSubFromDB.isPresent());
       Assertions.assertEquals(1, groupSubFromDB.get().getId());
       List<TelegramUser> users = groupSubFromDB.get().getUsers();
       for(int i=0; i<users.size(); i++) {
           Assertions.assertEquals(String.valueOf(i + 1), users.get(i).getChatId());
           Assertions.assertTrue(users.get(i).isActive());
       }
   }
}
Y el script fiveUsersForGroupSub.sql que falta:
INSERT INTO tg_user VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1);

INSERT INTO group_sub VALUES (1, 'g1', 1);

INSERT INTO group_x_user VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5);
En este punto, parte del trabajo con el repositorio se puede considerar completado. Ahora escribamos una capa de servicio.

Escribimos GroupSubService

En esta etapa, para trabajar con grupos de suscripciones, solo necesitamos poder guardarlas, así que no hay problema: creamos el servicio GroupSubService y su implementación de GroupSubServiceImpl en un paquete que contiene otros servicios - servicio:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;

/**
* Service for manipulating with {@link GroupSub}.
*/
public interface GroupSubService {

   GroupSub save(String chatId, GroupDiscussionInfo groupDiscussionInfo);
}
Y su implementación:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.GroupSubRepository;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.ws.rs.NotFoundException;
import java.util.Optional;

@Service
public class GroupSubServiceImpl implements GroupSubService {

   private final GroupSubRepository groupSubRepository;
   private final TelegramUserService telegramUserService;

   @Autowired
   public GroupSubServiceImpl(GroupSubRepository groupSubRepository, TelegramUserService telegramUserService) {
       this.groupSubRepository = groupSubRepository;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public GroupSub save(String chatId, GroupDiscussionInfo groupDiscussionInfo) {
       TelegramUser telegramUser = telegramUserService.findByChatId(chatId).orElseThrow(NotFoundException::new);
       //TODO add exception handling
       GroupSub groupSub;
       Optional<GroupSub> groupSubFromDB = groupSubRepository.findById(groupDiscussionInfo.getId());
       if(groupSubFromDB.isPresent()) {
           groupSub = groupSubFromDB.get();
           Optional<TelegramUser> first = groupSub.getUsers().stream()
                   .filter(it -> it.getChatId().equalsIgnoreCase(chatId))
                   .findFirst();
           if(first.isEmpty()) {
               groupSub.addUser(telegramUser);
           }
       } else {
           groupSub = new GroupSub();
           groupSub.addUser(telegramUser);
           groupSub.setId(groupDiscussionInfo.getId());
           groupSub.setTitle(groupDiscussionInfo.getTitle());
       }
       return groupSubRepository.save(groupSub);
   }
}
Para que Spring Data funcione correctamente y se cree un registro de muchos a muchos, necesitamos obtener el usuario de nuestra base de datos para el grupo de suscripción que estamos creando y agregarlo al objeto GroupSub. Así, cuando transfiramos esta suscripción para guardarla, también se creará una conexión a través de la tabla group_x_user. Puede surgir una situación en la que dicho grupo de suscripción ya se haya creado y solo necesite agregarle otro usuario. Para ello, primero obtenemos el ID del grupo de la base de datos, y si hay un registro trabajamos con él, si no, creamos uno nuevo. Es importante tener en cuenta que para trabajar con TelegramUser utilizamos TelegramUserService para seguir el último de los principios SOLID. Por el momento, si no encontramos un registro por ID, simplemente lanzo una excepción. No se está procesando de ninguna manera ahora: lo haremos al final, antes del MVP. Escribamos dos pruebas unitarias para la clase GroupSubServiceTest . ¿Cuáles necesitamos? Quiero estar seguro de que se llamará al método de guardar en GroupSubRepository y que se pasará una entidad con un solo usuario a GroupSub, la que nos devolverá TelegramUserService utilizando la ID proporcionada. Y la segunda opción es cuando un grupo con el mismo ID ya está en la base de datos y este grupo ya tiene un usuario, y necesita verificar que se agregará otro usuario a este grupo y este objeto se guardará. Aquí está la implementación:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.GroupSubRepository;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Optional;

@DisplayName("Unit-level testing for GroupSubService")
public class GroupSubServiceTest {

   private GroupSubService groupSubService;
   private GroupSubRepository groupSubRepository;
   private TelegramUser newUser;

   private final static String CHAT_ID = "1";

   @BeforeEach
   public void init() {
       TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
       groupSubRepository = Mockito.mock(GroupSubRepository.class);
       groupSubService = new GroupSubServiceImpl(groupSubRepository, telegramUserService);

       newUser = new TelegramUser();
       newUser.setActive(true);
       newUser.setChatId(CHAT_ID);

       Mockito.when(telegramUserService.findByChatId(CHAT_ID)).thenReturn(Optional.of(newUser));
   }

   @Test
   public void shouldProperlySaveGroup() {
       //given

       GroupDiscussionInfo groupDiscussionInfo = new GroupDiscussionInfo();
       groupDiscussionInfo.setId(1);
       groupDiscussionInfo.setTitle("g1");

       GroupSub expectedGroupSub = new GroupSub();
       expectedGroupSub.setId(groupDiscussionInfo.getId());
       expectedGroupSub.setTitle(groupDiscussionInfo.getTitle());
       expectedGroupSub.addUser(newUser);

       //when
       groupSubService.save(CHAT_ID, groupDiscussionInfo);

       //then
       Mockito.verify(groupSubRepository).save(expectedGroupSub);
   }

   @Test
   public void shouldProperlyAddUserToExistingGroup() {
       //given
       TelegramUser oldTelegramUser = new TelegramUser();
       oldTelegramUser.setChatId("2");
       oldTelegramUser.setActive(true);

       GroupDiscussionInfo groupDiscussionInfo = new GroupDiscussionInfo();
       groupDiscussionInfo.setId(1);
       groupDiscussionInfo.setTitle("g1");

       GroupSub groupFromDB = new GroupSub();
       groupFromDB.setId(groupDiscussionInfo.getId());
       groupFromDB.setTitle(groupDiscussionInfo.getTitle());
       groupFromDB.addUser(oldTelegramUser);

       Mockito.when(groupSubRepository.findById(groupDiscussionInfo.getId())).thenReturn(Optional.of(groupFromDB));

       GroupSub expectedGroupSub = new GroupSub();
       expectedGroupSub.setId(groupDiscussionInfo.getId());
       expectedGroupSub.setTitle(groupDiscussionInfo.getTitle());
       expectedGroupSub.addUser(oldTelegramUser);
       expectedGroupSub.addUser(newUser);

       //when
       groupSubService.save(CHAT_ID, groupDiscussionInfo);

       //then
       Mockito.verify(groupSubRepository).findById(groupDiscussionInfo.getId());
       Mockito.verify(groupSubRepository).save(expectedGroupSub);
   }

}
También agregué el método init() con la anotación BeforeEach. De esta manera, generalmente crean un método que se ejecutará antes de que se ejecute cada prueba, y es posible ponerle una lógica común para todas las pruebas. En nuestro caso, necesitamos bloquear TelegramUserService de la misma manera para todas las pruebas de esta clase, por lo que tiene sentido transferir esta lógica a un método común. Aquí se utilizan dos diseños de mokito:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - en él decimos que cuando se llama al método m1 en el objeto o1 con el argumento a1 , el método devolverá el objeto o2 . Esta es casi la funcionalidad más importante de Mockito: forzar al objeto simulado a devolver exactamente lo que necesitamos;

  • Mockito.verify(o1).m1(a1) : que verifica que se llamó al método m1 en el objeto o1 con el argumento a1 . Por supuesto, era posible utilizar el objeto devuelto por el método save, pero decidí hacerlo un poco más complicado mostrando otro método posible. ¿Cuándo puede ser útil? En los casos en que los métodos de clases simuladas devuelven void. Entonces sin Mockito.verify no habrá trabajo)))

Seguimos adheridos a la idea de que es necesario escribir pruebas, y muchas de ellas deben escribirse. La siguiente etapa es trabajar con el equipo de bots de Telegram.

Crea el comando /addGroupSub

Aquí debemos realizar la siguiente lógica: si recibimos solo un comando, sin ningún contexto, ayudamos al usuario y le damos una lista de todos los grupos con sus ID para que pueda pasar la información necesaria al bot. Y si el usuario envía un comando al bot con alguna otra palabra, busque un grupo con esa ID o escriba que no existe dicho grupo. Agreguemos un nuevo valor en nuestro nombre: CommandName:
ADD_GROUP_SUB("/addgroupsub")
Vayamos más allá de la base de datos al bot de Telegram: creemos la clase AddGroupSubCommand en el paquete de comandos:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.javarushclient.JavaRushGroupClient;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.service.GroupSubService;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.util.stream.Collectors;

import static com.github.javarushcommunity.jrtb.command.CommandName.ADD_GROUP_SUB;
import static com.github.javarushcommunity.jrtb.command.CommandUtils.getChatId;
import static com.github.javarushcommunity.jrtb.command.CommandUtils.getMessage;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.SPACE;
import static org.apache.commons.lang3.StringUtils.isNumeric;

/**
* Add Group subscription {@link Command}.
*/
public class AddGroupSubCommand implements Command {

   private final SendBotMessageService sendBotMessageService;
   private final JavaRushGroupClient javaRushGroupClient;
   private final GroupSubService groupSubService;

   public AddGroupSubCommand(SendBotMessageService sendBotMessageService, JavaRushGroupClient javaRushGroupClient,
                             GroupSubService groupSubService) {
       this.sendBotMessageService = sendBotMessageService;
       this.javaRushGroupClient = javaRushGroupClient;
       this.groupSubService = groupSubService;
   }

   @Override
   public void execute(Update update) {
       if (getMessage(update).equalsIgnoreCase(ADD_GROUP_SUB.getCommandName())) {
           sendGroupIdList(getChatId(update));
           return;
       }
       String groupId = getMessage(update).split(SPACE)[1];
       String chatId = getChatId(update);
       if (isNumeric(groupId)) {
           GroupDiscussionInfo groupById = javaRushGroupClient.getGroupById(Integer.parseInt(groupId));
           if (isNull(groupById.getId())) {
               sendGroupNotFound(chatId, groupId);
           }
           GroupSub savedGroupSub = groupSubService.save(chatId, groupById);
           sendBotMessageService.sendMessage(chatId, "Подписал на группу " + savedGroupSub.getTitle());
       } else {
           sendGroupNotFound(chatId, groupId);
       }
   }

   private void sendGroupNotFound(String chatId, String groupId) {
       String groupNotFoundMessage = "Нет группы с ID = \"%s\"";
       sendBotMessageService.sendMessage(chatId, String.format(groupNotFoundMessage, groupId));
   }

   private void sendGroupIdList(String chatId) {
       String groupIds = javaRushGroupClient.getGroupList(GroupRequestArgs.builder().build()).stream()
               .map(group -> String.format("%s - %s \n", group.getTitle(), group.getId()))
               .collect(Collectors.joining());

       String message = "Quéбы подписаться на группу - передай комадну вместе с ID группы. \n" +
               "Например: /addGroupSub 16. \n\n" +
               "я подготовил список всех групп - выберай Cómoую хочешь :) \n\n" +
               "Nombre группы - ID группы \n\n" +
               "%s";

       sendBotMessageService.sendMessage(chatId, String.format(message, groupIds));
   }
}
Esta clase usa el método isNumeric de la biblioteca apache-commons, así que agreguémoslo a nuestra memoria:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
Y en el bloque de propiedades:
<apache.commons.version>3.11</apache.commons.version>
Toda esta lógica está en la clase. Léalo detenidamente. Si tienes alguna pregunta/sugerencia, escríbela en los comentarios. Después de esto, necesitamos agregar el comando a CommandContainer en nuestro mapa de comandos:
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
Y todo por este equipo. Me gustaría probar de alguna manera esta funcionalidad, pero hasta ahora sólo puedo verla en la base de datos. En la tercera parte, agregaré cambios desde JRTB-6 para que podamos ver la lista de grupos a los que está suscrito un usuario. Ahora sería bueno comprobar todo esto. Para ello realizaremos todas las acciones en Telegram y consultaremos en la base de datos. Como tenemos pruebas escritas, todo debería estar bien. El artículo ya es bastante largo, por lo que escribiremos una prueba para AddGroupSubCommand más adelante y agregaremos TODO en el código para no olvidarlo.

conclusiones

En este artículo, analizamos el trabajo de agregar funcionalidad a toda la aplicación, comenzando desde la base de datos y terminando con el trabajo con el cliente que usa el bot. Por lo general, estas tareas ayudan a comprender el proyecto y comprender su esencia. Entiende cómo funciona. Estos días los temas no son fáciles, así que no seas tímido: escribe tus preguntas en los comentarios e intentaré responderlas. ¿Te gusta el proyecto? Dale una estrella en Github : así quedará claro que están interesados ​​en el proyecto y yo estaré feliz. Como suele decirse, un maestro siempre se alegra cuando se valora su trabajo. El código contendrá las tres partes de STEP_6 y estará disponible antes de este artículo. ¿Cómo saberlo? Es fácil: únete al canal de Telegram , donde publico toda la información sobre mis artículos sobre el bot de Telegram. ¡Gracias por leer! La parte 3 ya está aquí .

Al principio de este artículo encontrará una lista de todos los materiales de la serie.

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