Hola a todos, mis queridos amigos, futuros ingenieros de software senior. Seguimos desarrollando un bot de Telegram. En este paso de nuestro proyecto, consideraremos tres tareas que tienen un valor más visible que el valor del programa. Necesitamos aprender cómo eliminar una suscripción a nuevos artículos de un grupo específico: use el comando /stop para desactivar el bot y use el comando /start para activarlo. Además, todas las solicitudes y actualizaciones conciernen únicamente a los usuarios activos del bot. Como de costumbre, actualizaremos la rama principal para obtener todos los cambios y crear una nueva: STEP_7_JRTB-7. En esta parte, hablaremos sobre cómo eliminar una suscripción y consideraremos 5 opciones para eventos; será interesante.
Actualizamos la versión de nuestro proyecto a 0.6.0-SNAPSHOT Actualizamos RELEASE_NOTES.md, añadiendo una descripción de lo realizado en la nueva versión:
dale me gusta, suscríbete, toca el timbre, dale una estrella a nuestro proyecto , escribe comentarios y califica el artículo. Gracias a todos. Hasta la siguiente parte. Pronto hablaremos sobre cómo agregar la desactivación y activación de bots mediante los comandos /stop & /start y cómo usarlos mejor. ¡Hasta luego!
JRTB-7: eliminar una suscripción a nuevos artículos de un grupo
Está claro que todos los usuarios querrán poder eliminar su suscripción para no recibir notificaciones sobre nuevos artículos. Su lógica será muy similar a la lógica de agregar una suscripción. Si enviamos solo un comando, en respuesta recibiremos una lista de grupos y sus ID a los que el usuario ya está suscrito, para que podamos entender qué es exactamente lo que se debe eliminar. Y si el usuario envía el ID del grupo junto con el equipo, eliminaremos la suscripción. Por lo tanto, desarrollemos este comando desde el lado del bot de Telegram.-
Agreguemos el nombre del nuevo comando - /deleteGroupSub , y en CommandName - la línea:
DELETE_GROUP_SUB("/deleteGroupSub")
-
A continuación, creemos el comando DeleteGroupSubCommand :
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.GroupSubService; import com.github.javarushcommunity.jrtb.service.SendBotMessageService; import com.github.javarushcommunity.jrtb.service.TelegramUserService; import org.springframework.util.CollectionUtils; import org.telegram.telegrambots.meta.api.objects.Update; import javax.ws.rs.NotFoundException; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import static com.github.javarushcommunity.jrtb.command.CommandName.DELETE_GROUP_SUB; import static com.github.javarushcommunity.jrtb.command.CommandUtils.getChatId; import static com.github.javarushcommunity.jrtb.command.CommandUtils.getMessage; import static java.lang.String.format; import static org.apache.commons.lang3.StringUtils.SPACE; import static org.apache.commons.lang3.StringUtils.isNumeric; /** * Delete Group subscription {@link Command}. */ public class DeleteGroupSubCommand implements Command { private final SendBotMessageService sendBotMessageService; private final TelegramUserService telegramUserService; private final GroupSubService groupSubService; public DeleteGroupSubCommand(SendBotMessageService sendBotMessageService, GroupSubService groupSubService, TelegramUserService telegramUserService) { this.sendBotMessageService = sendBotMessageService; this.groupSubService = groupSubService; this.telegramUserService = telegramUserService; } @Override public void execute(Update update) { if (getMessage(update).equalsIgnoreCase(DELETE_GROUP_SUB.getCommandName())) { sendGroupIdList(getChatId(update)); return; } String groupId = getMessage(update).split(SPACE)[1]; String chatId = getChatId(update); if (isNumeric(groupId)) { Optional<GroupSub> optionalGroupSub = groupSubService.findById(Integer.valueOf(groupId)); if (optionalGroupSub.isPresent()) { GroupSub groupSub = optionalGroupSub.get(); TelegramUser telegramUser = telegramUserService.findByChatId(chatId).orElseThrow(NotFoundException::new); groupSub.getUsers().remove(telegramUser); groupSubService.save(groupSub); sendBotMessageService.sendMessage(chatId, format("Удалил подписку на группу: %s", groupSub.getTitle())); } else { sendBotMessageService.sendMessage(chatId, "Не нашел такой группы =/"); } } else { sendBotMessageService.sendMessage(chatId, "неправильный формат ID группы.\n " + "ID должно быть целым положительным числом"); } } private void sendGroupIdList(String chatId) { String message; List<GroupSub> groupSubs = telegramUserService.findByChatId(chatId) .orElseThrow(NotFoundException::new) .getGroupSubs(); if (CollectionUtils.isEmpty(groupSubs)) { message = "Пока нет подписок на группы. Quéбы добавить подписку напиши /addGroupSub"; } else { message = "Quéбы удалить подписку на группу - передай комадну вместе с ID группы. \n" + "Например: /deleteGroupSub 16 \n\n" + "я подготовил список всех групп, на которые ты подписан) \n\n" + "Nombre группы - ID группы \n\n" + "%s"; } String userGroupSubData = groupSubs.stream() .map(group -> format("%s - %s \n", group.getTitle(), group.getId())) .collect(Collectors.joining()); sendBotMessageService.sendMessage(chatId, format(message, userGroupSubData)); } }
@Data
@Entity
@Table(name = "tg_user")
@EqualsAndHashCode(exclude = "groupSubs")
public class TelegramUser {
@Id
@Column(name = "chat_id")
private String chatId;
@Column(name = "active")
private boolean active;
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
}
Como resultado, todo salió como queríamos. Hay varios eventos posibles en un equipo, por lo que escribir una buena prueba para cada uno de ellos es una gran idea. Hablando de pruebas: mientras las escribía, encontré un defecto en la lógica y lo corregí antes de que fuera lanzado a producción. Si no se hubiera realizado ninguna prueba, no está claro qué tan rápido se habría detectado. EliminarGroupSubCommandTest:
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.GroupSubService;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.telegram.telegrambots.meta.api.objects.Update;
import java.util.ArrayList;
import java.util.Optional;
import static com.github.javarushcommunity.jrtb.command.AbstractCommandTest.prepareUpdate;
import static com.github.javarushcommunity.jrtb.command.CommandName.DELETE_GROUP_SUB;
import static java.util.Collections.singletonList;
@DisplayName("Unit-level testing for DeleteGroupSubCommand")
class DeleteGroupSubCommandTest {
private Command command;
private SendBotMessageService sendBotMessageService;
GroupSubService groupSubService;
TelegramUserService telegramUserService;
@BeforeEach
public void init() {
sendBotMessageService = Mockito.mock(SendBotMessageService.class);
groupSubService = Mockito.mock(GroupSubService.class);
telegramUserService = Mockito.mock(TelegramUserService.class);
command = new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService);
}
@Test
public void shouldProperlyReturnEmptySubscriptionList() {
//given
Long chatId = 23456L;
Update update = prepareUpdate(chatId, DELETE_GROUP_SUB.getCommandName());
Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
.thenReturn(Optional.of(new TelegramUser()));
String expectedMessage = "Пока нет подписок на группы. Quéбы добавить подписку напиши /addGroupSub";
//when
command.execute(update);
//then
Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
}
@Test
public void shouldProperlyReturnSubscriptionLit() {
//given
Long chatId = 23456L;
Update update = prepareUpdate(chatId, DELETE_GROUP_SUB.getCommandName());
TelegramUser telegramUser = new TelegramUser();
GroupSub gs1 = new GroupSub();
gs1.setId(123);
gs1.setTitle("GS1 Title");
telegramUser.setGroupSubs(singletonList(gs1));
Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
.thenReturn(Optional.of(telegramUser));
String expectedMessage = "Quéбы удалить подписку на группу - передай комадну вместе с ID группы. \n" +
"Например: /deleteGroupSub 16 \n\n" +
"я подготовил список всех групп, на которые ты подписан) \n\n" +
"Nombre группы - ID группы \n\n" +
"GS1 Title - 123 \n";
//when
command.execute(update);
//then
Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
}
@Test
public void shouldRejectByInvalidGroupId() {
//given
Long chatId = 23456L;
Update update = prepareUpdate(chatId, String.format("%s %s", DELETE_GROUP_SUB.getCommandName(), "groupSubId"));
TelegramUser telegramUser = new TelegramUser();
GroupSub gs1 = new GroupSub();
gs1.setId(123);
gs1.setTitle("GS1 Title");
telegramUser.setGroupSubs(singletonList(gs1));
Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
.thenReturn(Optional.of(telegramUser));
String expectedMessage = "неправильный формат ID группы.\n " +
"ID должно быть целым положительным числом";
//when
command.execute(update);
//then
Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
}
@Test
public void shouldProperlyDeleteByGroupId() {
//given
/// prepare update object
Long chatId = 23456L;
Integer groupId = 1234;
Update update = prepareUpdate(chatId, String.format("%s %s", DELETE_GROUP_SUB.getCommandName(), groupId));
GroupSub gs1 = new GroupSub();
gs1.setId(123);
gs1.setTitle("GS1 Title");
TelegramUser telegramUser = new TelegramUser();
telegramUser.setChatId(chatId.toString());
telegramUser.setGroupSubs(singletonList(gs1));
ArrayList<TelegramUser> users = new ArrayList<>();
users.add(telegramUser);
gs1.setUsers(users);
Mockito.when(groupSubService.findById(groupId)).thenReturn(Optional.of(gs1));
Mockito.when(telegramUserService.findByChatId(String.valueOf(chatId)))
.thenReturn(Optional.of(telegramUser));
String expectedMessage = "Удалил подписку на группу: GS1 Title";
//when
command.execute(update);
//then
users.remove(telegramUser);
Mockito.verify(groupSubService).save(gs1);
Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
}
@Test
public void shouldDoesNotExistByGroupId() {
//given
Long chatId = 23456L;
Integer groupId = 1234;
Update update = prepareUpdate(chatId, String.format("%s %s", DELETE_GROUP_SUB.getCommandName(), groupId));
Mockito.when(groupSubService.findById(groupId)).thenReturn(Optional.empty());
String expectedMessage = "Не нашел такой группы =/";
//when
command.execute(update);
//then
Mockito.verify(groupSubService).findById(groupId);
Mockito.verify(sendBotMessageService).sendMessage(chatId.toString(), expectedMessage);
}
}
Aquí, cada prueba verifica un escenario separado y, permítanme recordarles, solo hay cinco:
- cuando simplemente pasó el comando /deleteGroupSub y no hay suscripciones de grupo;
- cuando simplemente pasó el comando /deleteGroupSub y hay suscripciones a grupos;
- cuando se pasó un ID de grupo no válido, por ejemplo, /deleteGroupSub abc ;
- un escenario en el que todo se elimina correctamente, como se esperaba;
- un escenario en el que el ID del grupo es válido, pero dicho grupo no está en la base de datos.
.put(DELETE_GROUP_SUB.getCommandName(),
new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService))
Ahora puedes probar la funcionalidad en un bot de prueba. Lanzamos nuestra base de datos usando docker-compose-test.yml: docker-compose -f docker-compose-test.yml up Y lanzamos SpringBoot a través de IDEA. Borraré por completo la correspondencia con el bot y empezaré de nuevo. Revisaré todas las opciones que puedan surgir al trabajar con este equipo. Como puede ver en la captura de pantalla, todas las opciones se realizaron y tuvieron éxito.
¡Amigos! ¿Quiere saber inmediatamente cuándo se publica un nuevo código para un proyecto? ¿Cuándo sale un nuevo artículo? Únete a mi canal de Telegram . Allí recopilo mis artículos, pensamientos y desarrollo de código abierto. |
## 0.6.0-SNAPSHOT * JRTB-7: se agregó la capacidad de eliminar la suscripción grupal.
El código funciona, se han escrito pruebas para él: es hora de enviar la tarea al repositorio y crear una solicitud de extracción.
GO TO FULL VERSION