大家好,親愛的朋友們,未來的資深軟體工程師。我們繼續開發電報機器人。在我們專案的這一步中,我們將研究比軟體價值更明顯的價值的三個任務。我們需要了解如何從特定群組中刪除新文章的訂閱:使用/stop命令停用機器人,並使用/start命令啟動它。因此,所有請求和更新僅涉及機器人的活躍用戶。像往常一樣,我們將更新主分支以獲取所有變更並建立新分支:STEP_7_JRTB-7。在這一部分中,我們將討論刪除訂閱並考慮 5 個事件選項 - 這將會很有趣。
我們將專案的版本更新為 0.6.0-SNAPSHOT 我們更新 RELEASE_NOTES.md,新增版本中所做操作的描述:
喜歡 - 訂閱 - 按響鈴,給我們的項目一顆星,寫評論並評論文章! 謝謝大家。直到下一部分。我們很快就會討論如何透過/stop和/start命令添加機器人停用和啟動以及如何最好地使用它們。回頭見!
JRTB-7:從群組中刪除新文章的訂閱
顯然,所有用戶都希望能夠刪除他們的訂閱,以免收到有關新文章的通知。它的邏輯與添加訂閱的邏輯非常相似。如果我們只發送一個命令,作為回應,我們將收到用戶已訂閱的群組及其 ID 的列表,以便我們了解到底需要刪除哪些內容。如果使用者將群組 ID 與團隊一起傳送,我們將刪除訂閱。因此,讓我們從電報機器人端開發這個指令。-
讓我們新增指令的名稱 - /deleteGroupSub,並在CommandName中新增以下行:
DELETE_GROUP_SUB("/deleteGroupSub")
-
接下來,讓我們建立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 = "Пока нет подписок на группы. Whatбы добавить подписку напиши /addGroupSub"; } else { message = "Whatбы удалить подписку на группу - передай комадну вместе с ID группы. \n" + "Например: /deleteGroupSub 16 \n\n" + "я подготовил список всех групп, на которые ты подписан) \n\n" + "Name группы - 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;
}
結果,一切都如我們所願。團隊中有幾個可能的事件,因此為每個事件編寫一個好的測試是一個好主意。說到測試:當我編寫測試時,我發現了邏輯上的缺陷,並在發佈到生產環境之前修正了它。如果沒有進行測試,目前還不清楚它會多快被發現。刪除群組子命令測試:
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 = "Пока нет подписок на группы. Whatбы добавить подписку напиши /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 = "Whatбы удалить подписку на группу - передай комадну вместе с ID группы. \n" +
"Например: /deleteGroupSub 16 \n\n" +
"я подготовил список всех групп, на которые ты подписан) \n\n" +
"Name группы - 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);
}
}
在這裡,每個測試都會檢查一個單獨的場景,讓我提醒您,只有其中五個:
- 當您只是傳遞/deleteGroupSub命令並且沒有群組訂閱時;
- 當您簡單地傳遞/deleteGroupSub命令並且有群組訂閱時;
- 當傳遞無效群組 ID 時,例如/deleteGroupSub abc;
- 正如預期的那樣,所有內容都被正確刪除的場景;
- 組 ID 有效,但資料庫中不存在這樣的組的情況。
.put(DELETE_GROUP_SUB.getCommandName(),
new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService))
現在您可以在測試機器人上測試功能。我們使用 docker-compose-test.yml 啟動資料庫: docker-compose -f docker-compose-test.yml up 並透過 IDEA 啟動 SpringBoot。我將徹底清除與機器人的通訊並重新開始。我將詳細介紹與該團隊合作時可能出現的所有選項。從螢幕截圖中可以看出,所有選項均已通過並且成功。
朋友們!您想立即知道專案的新程式碼何時發布嗎?新文章什麼時候出來?加入我的Telegram 頻道。我在那裡收集我的文章、想法和開源開發。 |
## 0.6.0-SNAPSHOT * JRTB-7:新增了刪除群組訂閱的功能。
程式碼可以工作,已經為其編寫了測試:是時候將任務推送到儲存庫並建立拉取請求了。
GO TO FULL VERSION