JavaRush /Java Blog /Random-ID /Kami menghapus langganan artikel dari grup - "Proyek Java...

Kami menghapus langganan artikel dari grup - "Proyek Java dari A hingga Z"

Dipublikasikan di grup Random-ID
Halo semuanya, teman-teman terkasih, Insinyur Perangkat Lunak Senior masa depan. Kami terus mengembangkan bot telegram. Pada langkah proyek kami ini, kami akan mempertimbangkan tiga tugas yang memiliki nilai lebih terlihat daripada nilai program. Kita perlu mempelajari cara menghapus langganan artikel baru dari grup tertentu: gunakan perintah /stop untuk menonaktifkan bot, dan gunakan perintah /start untuk mengaktifkannya. Selain itu, semua permintaan dan pembaruan hanya berlaku untuk pengguna bot aktif. Seperti biasa, kami akan memperbarui cabang utama untuk mendapatkan semua perubahan dan membuat yang baru: STEP_7_JRTB-7. Di bagian ini, kita akan berbicara tentang menghapus langganan dan mempertimbangkan 5 opsi acara - ini akan menarik.

JRTB-7: menghapus langganan artikel baru dari grup

Jelas bahwa semua pengguna ingin menghapus langganan mereka agar tidak menerima pemberitahuan tentang artikel baru. Logikanya akan sangat mirip dengan logika menambahkan langganan. Jika kami hanya mengirim satu perintah, sebagai tanggapan kami akan menerima daftar grup dan ID mereka yang sudah menjadi langganan pengguna, sehingga kami dapat memahami apa sebenarnya yang perlu dihapus. Dan jika pengguna mengirimkan ID grup beserta tim, langganan akan kami hapus. Oleh karena itu, mari kita kembangkan perintah ini dari sisi bot telegram.
  1. Mari tambahkan nama perintah baru - /deleteGroupSub , dan di CommandName - baris:

    DELETE_GROUP_SUB("/deleteGroupSub")

  2. Selanjutnya, mari kita buat perintah 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));
       }
    }

Untuk melakukan ini, kami harus menambahkan dua metode lagi untuk bekerja dengan entitas GroupSub - mengambil dari database berdasarkan ID dan menyimpan entitas itu sendiri. Semua metode ini hanya memanggil metode repositori yang sudah jadi. Saya akan memberi tahu Anda secara terpisah tentang menghapus langganan. Dalam skema database, ini adalah tabel yang bertanggung jawab atas proses banyak-ke-banyak, dan untuk menghapus hubungan ini, Anda perlu menghapus catatan di dalamnya. Hal ini jika kita menggunakan pengertian umum pada bagian database. Tapi kami menggunakan Spring Data dan ada Hibernate secara default, yang dapat melakukan ini secara berbeda. Kami mendapatkan entitas GroupSub, yang mana semua pengguna yang terkait dengannya akan ditarik. Dari kumpulan pengguna ini kami akan menghapus pengguna yang kami perlukan dan menyimpan groupSub kembali ke database, tetapi tanpa pengguna ini. Dengan cara ini Spring Data akan memahami apa yang kita inginkan dan menghapus catatan tersebut. "Proyek Java dari A hingga Z": Menghapus langganan artikel dari grup - 1"Proyek Java dari A hingga Z": Menghapus langganan artikel dari grup - 2Untuk menghapus pengguna dengan cepat, saya menambahkan anotasi EqualsAndHashCode untuk TelegramUser, tidak termasuk daftar GroupSub sehingga tidak ada masalah. Dan disebut metode hapus pada kumpulan pengguna dengan pengguna yang kita butuhkan. Ini tampilannya untuk Pengguna Telegram:
@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;
}
Hasilnya, semuanya berjalan sesuai keinginan kami. Ada beberapa kemungkinan kejadian dalam sebuah tim, jadi menulis tes yang bagus untuk masing-masing kejadian adalah ide yang bagus. Berbicara tentang tes: ketika saya menulisnya, saya menemukan cacat logika dan memperbaikinya sebelum dirilis ke produksi. Jika tidak ada tes, tidak jelas seberapa cepat penyakit itu bisa terdeteksi. HapusGroupSubCommandTest:
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);
   }
}
Di sini, setiap pengujian memeriksa skenario terpisah, dan izinkan saya mengingatkan Anda, hanya ada lima skenario:
  • ketika Anda hanya memberikan perintah /deleteGroupSub dan tidak ada langganan grup;
  • ketika Anda baru saja meneruskan perintah /deleteGroupSub dan ada langganan ke grup;
  • ketika ID grup yang tidak valid diberikan, misalnya /deleteGroupSub abc ;
  • skenario di mana semuanya dihapus dengan benar, seperti yang diharapkan;
  • skenario ketika ID grup valid, namun grup tersebut tidak ada dalam database.
Seperti yang Anda lihat, semua skenario ini perlu diatasi dengan pengujian. Saat saya menulis, saya menyadari bahwa untuk menulis tes yang lebih baik, ada baiknya mengikuti beberapa kursus pengujian. Saya pikir ini akan membantu untuk mencari opsi yang berbeda dengan benar. Itu benar, pemikiran untuk masa depan. Selanjutnya, Anda perlu menambahkan deskripsi ke perintah /help bahwa Anda sekarang dapat menghapus langganan. Mari kita letakkan di bagian untuk bekerja dengan langganan. "Proyek Java dari A hingga Z": Menghapus langganan artikel dari grup - 3Tentu saja, agar perintah ini berfungsi, Anda perlu menambahkan inisialisasinya ke CommandContainer :
.put(DELETE_GROUP_SUB.getCommandName(),
       new DeleteGroupSubCommand(sendBotMessageService, groupSubService, telegramUserService))
Sekarang Anda dapat menguji fungsionalitasnya pada bot uji. Kami meluncurkan database kami menggunakan docker-compose-test.yml: docker-compose -f docker-compose-test.yml up Dan meluncurkan SpringBoot melalui IDEA. Saya akan menghapus korespondensi dengan bot sepenuhnya dan memulai lagi. Saya akan membahas semua opsi yang mungkin muncul saat bekerja dengan tim ini. "Proyek Java dari A hingga Z": Menghapus langganan artikel dari grup - 4Seperti yang Anda lihat dari tangkapan layar, semua opsi berhasil dan berhasil.
Teman-teman! Apakah Anda ingin segera mengetahui kapan kode baru untuk suatu proyek dirilis? Kapan artikel baru keluar? Bergabunglah dengan saluran Telegram saya . Di sana saya mengumpulkan artikel, pemikiran, dan pengembangan open source saya bersama-sama.
Kami memperbarui versi proyek kami ke 0.6.0-SNAPSHOT. Kami memperbarui RELEASE_NOTES.md, menambahkan deskripsi tentang apa yang telah dilakukan di versi baru:
## 0.6.0-SNAPSHOT * JRTB-7: menambahkan kemampuan untuk menghapus langganan grup.
Kode berfungsi, pengujian telah ditulis untuknya: saatnya memasukkan tugas ke dalam repositori dan membuat permintaan tarik."Proyek Java dari A hingga Z": Menghapus langganan artikel dari grup - 5

Bukannya berakhir

Kami sudah lama tidak melihat papan proyek kami, tetapi ada perubahan besar: "Proyek Java dari A hingga Z": Menghapus langganan artikel dari grup - 6Hanya tersisa 5 tugas. Artinya, Anda dan saya sudah berada di ujung jalan. Tersisa sedikit. Menariknya, rangkaian artikel ini telah berjalan sejak pertengahan September, yakni selama 7 bulan!!! Saat saya mendapatkan ide ini, saya tidak menyangka akan memakan waktu lama. Pada saat yang sama, saya sangat senang dengan hasilnya! Sobat, jika kurang jelas apa yang terjadi di artikel tersebut, ajukan pertanyaan di kolom komentar. Dengan cara ini saya akan mengetahui bahwa ada sesuatu yang perlu dijelaskan dengan lebih baik, dan ada sesuatu yang memerlukan penjelasan lebih lanjut. Nah, seperti biasa, suka - berlangganan - bunyikan bel, beri bintang pada proyek kami , tulis komentar, dan beri peringkat artikel! Terimakasih untuk semua. Sampai di bagian selanjutnya. Kami akan segera berbicara tentang cara menambahkan penonaktifan dan aktivasi bot melalui perintah /stop & /start dan cara terbaik menggunakannya. Sampai jumpa lagi!

Daftar semua materi dalam seri ini ada di awal artikel ini.

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