JavaRush /جاوا بلاگ /Random-UR /ہم مضامین کے گروپ کو سبسکرائب کرنے کی صلاحیت کو شامل کر ر...

ہم مضامین کے گروپ کو سبسکرائب کرنے کی صلاحیت کو شامل کر رہے ہیں۔ (حصہ 2) - "A سے Z تک جاوا پروجیکٹ"

گروپ میں شائع ہوا۔
سب کو سلام! ہم اس کام پر کام جاری رکھے ہوئے ہیں جو ہم نے پچھلے ہفتے شروع کیا تھا ۔"A سے Z تک جاوا پروجیکٹ": مضامین کے گروپ کو سبسکرائب کرنے کی صلاحیت کو شامل کرنا۔  حصہ 2 - 1

ہم JRTB-5 نافذ کرتے ہیں۔

اب ہمیں ایک کمانڈ شامل کرنے کی ضرورت ہے تاکہ ہم جاوا رش سے مضامین کے کچھ گروپ کو سبسکرائب کر سکیں۔ یہ کیسے کرنا ہے؟ ہم سب سے آسان منظر نامے کی پیروی کریں گے جس کے ساتھ میں آیا ہوں۔ چونکہ ہمارے پاس گروپ ID کے ذریعے رسائی ہے، ہمیں صارف کی ضرورت ہے کہ وہ اسے منتقل کرے۔ ایسا کرنے کے لیے، صارف کمانڈ داخل کرے گا /addGroupSub GROUP_ID، جو دو طریقوں میں سے ایک میں کام کرے گا: اگر صرف کمانڈ خود آتا ہے: /addGroupSub ، تمام گروپوں کی فہرست اور ان کی IDs جواب میں بھیجی جاتی ہے۔ پھر صارف اپنے مطلوبہ گروپ ID کو منتخب کر سکے گا اور اس کمانڈ میں درخواست کا دوسرا ورژن بنا سکے گا: /addGroupSub GROUP_ID - اور پھر اس صارف کے پاس اس گروپ کا ریکارڈ ہوگا۔ مجھے لگتا ہے کہ ہم مستقبل میں بہتر کر سکتے ہیں۔ ہمارا مقصد ترقی کو دکھانا ہے، نہ کہ انتہائی ٹھنڈا صارف کا تجربہ (مجھے یہ کہتے ہوئے شرم آتی ہے، لیکن مجھے روسی زبان کی اصطلاح نہیں معلوم جس کا مطلب یہ ہو گا)۔ پوری ایپلیکیشن (ہمارے معاملے میں، ٹیلیگرام بوٹ کلائنٹ سے لے کر ڈیٹا بیس تک) کی فعالیت کو صحیح طریقے سے شامل کرنے کے لیے، آپ کو کسی سرے سے شروع کرنے کی ضرورت ہے۔ ہم یہ ڈیٹا بیس کی طرف سے کریں گے۔

ڈیٹا بیس میں ایک نئی منتقلی شامل کرنا

ایسا کرنے کا پہلا کام یہ ہے کہ ایک نیا ڈیٹا بیس منتقلی اور JR میں صارف گروپ سبسکرپشن ڈیٹا کو محفوظ کرنے کی صلاحیت شامل کی جائے۔ یہ یاد رکھنے کے لیے کہ یہ کیسا ہونا چاہیے، مضمون " پروجیکٹ پلاننگ: پیمائش سات بار " پر واپس جائیں۔ وہاں دوسری تصویر میں ڈیٹا بیس کا تخمینی خاکہ ہے۔ گروپ کی معلومات کو ذخیرہ کرنے کے لیے ہمیں ایک ٹیبل شامل کرنے کی ضرورت ہے:
  • JavaRush میں گروپ ID بھی ہماری ID ہوگی۔ ہم ان پر بھروسہ کرتے ہیں اور یقین رکھتے ہیں کہ یہ IDs منفرد ہیں۔
  • عنوان - ہماری تصویروں میں یہ نام تھا - گروپ کا غیر رسمی نام؛ یعنی، جو ہم JavaRush ویب سائٹ پر دیکھتے ہیں؛
  • last_article_id - اور یہ ایک دلچسپ فیلڈ ہے۔ یہ اس گروپ میں آرٹیکل کی آخری آئی ڈی کو اسٹور کرے گا، جسے بوٹ پہلے ہی اپنے سبسکرائبرز کو بھیج چکا ہے۔ اس فیلڈ کو استعمال کرتے ہوئے، نئے مضامین کی تلاش کا طریقہ کار کام کرے گا۔ نئے سبسکرائبرز کو صارف کے سبسکرائب کرنے سے پہلے شائع ہونے والے مضامین موصول نہیں ہوں گے: صرف وہی جو گروپ کو سبسکرائب کرنے کے بعد شائع ہوئے تھے۔
ہمارے پاس گروپس اور یوزرز ٹیبل کے درمیان کئی سے کئی کا رشتہ بھی ہوگا، کیونکہ ہر صارف کے پاس بہت سے گروپ سبسکرپشن (ایک سے کئی) ہوسکتے ہیں، اور ہر گروپ سبسکرپشن میں بہت سے صارفین ہوسکتے ہیں (ایک سے کئی، صرف دوسری طرف). پتہ چلتا ہے کہ یہ ہمارا بہت سے زیادہ ہوگا۔ ان لوگوں کے لیے جن کے سوالات ہیں، ڈیٹا بیس میں موجود مضامین کا جائزہ لیں۔ ہاں، میں جلد ہی ٹیلی گرام چینل پر ایک پوسٹ بنانے کا ارادہ کر رہا ہوں، جہاں میں ڈیٹا بیس پر تمام مضامین کو اکٹھا کروں گا۔ ہمارے دوسرے ڈیٹا بیس کی منتقلی کی طرح نظر آئے گا۔
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)
);
یہ نوٹ کرنا ضروری ہے کہ پہلے میں پرانے ٹیبل کو تبدیل کرتا ہوں - میں اس میں ایک بنیادی کلید شامل کرتا ہوں۔ اس وقت میں نے کسی نہ کسی طرح یہ یاد کیا تھا، لیکن اب MySQL نے مجھے gorup_x_user ٹیبل کے لیے فارن کلید شامل کرنے کا موقع نہیں دیا، اور اس منتقلی کے حصے کے طور پر میں نے ڈیٹا بیس کو اپ ڈیٹ کیا۔ براہ کرم ایک اہم پہلو نوٹ کریں۔ ڈیٹا بیس کو تبدیل کرنا بالکل اسی طرح کیا جانا چاہئے - ہر وہ چیز جس کی ضرورت ہے نئی منتقلی میں ہے، لیکن پہلے سے جاری کردہ منتقلی کو اپ ڈیٹ کرکے نہیں۔ ہاں، ہمارے معاملے میں کچھ نہیں ہوگا، کیونکہ یہ ایک ٹیسٹ پروجیکٹ ہے اور ہم جانتے ہیں کہ اسے صرف ایک جگہ پر تعینات کیا گیا ہے، لیکن یہ غلط طریقہ ہوگا۔ لیکن ہم چاہتے ہیں کہ سب کچھ درست ہو۔ اگلا ٹیبل بنانے سے پہلے ڈیلیٹ کرنا آتا ہے۔ یہ کیوں ہے؟ تاکہ اگر کسی موقع پر ڈیٹا بیس میں ایسے ناموں والی میزیں موجود ہوں تو منتقلی ناکام نہ ہو اور توقع کے عین مطابق کام کرے۔ اور پھر ہم دو میزیں شامل کرتے ہیں۔ سب کچھ ویسا ہی تھا جیسا ہم چاہتے تھے۔ اب ہمیں اپنی درخواست شروع کرنے کی ضرورت ہے۔ اگر سب کچھ شروع ہوتا ہے اور نہیں ٹوٹتا ہے، تو نقل مکانی ریکارڈ کی جاتی ہے۔ اور اسے دو بار چیک کرنے کے لیے، ہم ڈیٹا بیس پر جاتے ہیں تاکہ یہ یقینی بنایا جا سکے کہ: a) اس طرح کی میزیں نمودار ہوئی ہیں۔ b) فلائی وے ٹیکنیکل ٹیبل میں ایک نئی انٹری ہے۔ یہ ہجرت کا کام مکمل کرتا ہے، آئیے ذخیروں کی طرف چلتے ہیں۔

مخزن کی پرت شامل کرنا

اسپرنگ بوٹ ڈیٹا کی بدولت، یہاں سب کچھ بہت آسان ہے: ہمیں GroupSub entity کو شامل کرنا ہوگا، TelegramUser کو تھوڑا سا اپ ڈیٹ کرنا ہوگا اور تقریباً خالی GroupSubRepository کو شامل کرنا ہوگا: ہم GroupSub entity کو 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);
   }
}
ایک بات قابل غور ہے کہ ہمارے پاس ایک اضافی یوزرز فیلڈ ہے جس میں گروپ میں سبسکرائب کیے گئے تمام صارفین کا مجموعہ ہوگا۔ اور دو تشریحات - ManyToMany اور JoinTable - بالکل وہی ہیں جو ہمیں اس کے لئے درکار ہیں۔ اسی فیلڈ کو ٹیلیگرام یوزر کے لیے شامل کرنے کی ضرورت ہے:
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
یہ فیلڈ گروپسب ہستی میں لکھے گئے جوائنز کا استعمال کرتی ہے۔ اور، درحقیقت، گروپسب کے لیے ہماری ریپوزٹری کلاس ہے 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> {
}
اس مرحلے پر، ہمیں اضافی طریقوں کی ضرورت نہیں ہے: وہ جو JpaRepository آباؤ اجداد میں لاگو کیے گئے ہیں وہ ہمارے لیے کافی ہیں۔ آئیے TelegramUserRepositoryIT میں ایک ٹیسٹ لکھتے ہیں جو چیک کرے گا کہ ہمارے کئی سے زیادہ کام ہوتے ہیں۔ ٹیسٹ کا خیال یہ ہے کہ ہم ایک sql اسکرپٹ کے ذریعے ڈیٹا بیس میں فی صارف سبسکرپشنز کے 5 گروپس شامل کریں گے، اس صارف کو اس کی ID کے ذریعے حاصل کریں گے اور چیک کریں گے کہ ہمیں بالکل وہی گروپس اور بالکل یکساں اقدار کے ساتھ موصول ہوئے ہیں۔ یہ کیسے کرنا ہے؟ آپ ڈیٹا میں کاؤنٹر ایمبیڈ کر سکتے ہیں، جس کے بعد ہم جا کر چیک کر سکتے ہیں۔ یہ ہے 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);
اور خود ٹیسٹ:
@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());
   }
}
اب گروپسب ہستی کے لیے اسی معنی کا ایک ٹیسٹ شامل کریں۔ ایسا کرنے کے لیے، آئیے اسی پیکیج میں ایک ٹیسٹ کلاس گروپSubRepositoryIT بنائیں جس طرح گروپسب ریپوزٹری آئی ٹی :
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());
       }
   }
}
اور غائب FiveUsersForGroupSub.sql اسکرپٹ:
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);
اس وقت، ذخیرہ کے ساتھ کام کا حصہ مکمل سمجھا جا سکتا ہے. اب سروس لیئر لکھتے ہیں۔

ہم GroupSubService لکھتے ہیں۔

اس مرحلے پر، سبسکرپشن کے گروپوں کے ساتھ کام کرنے کے لیے، ہمیں صرف ان کو محفوظ کرنے کے قابل ہونے کی ضرورت ہے، اس لیے کوئی حرج نہیں: ہم گروپسب سروس سروس بناتے ہیں اور اس کے نفاذ کو ایک ایسے پیکج میں گروپسب سرویس آئی ایم پی ایل بناتے ہیں جس میں دیگر خدمات شامل ہیں - سروس:
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);
}
اور اس کا نفاذ:
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);
   }
}
اسپرنگ ڈیٹا کو صحیح طریقے سے کام کرنے اور کئی سے زیادہ ریکارڈ بنانے کے لیے، ہمیں صارف کو اپنے ڈیٹا بیس سے اس سبسکرپشن گروپ کے لیے حاصل کرنے کی ضرورت ہے جسے ہم بنا رہے ہیں اور اسے GroupSub آبجیکٹ میں شامل کریں۔ اس طرح، جب ہم اس سبسکرپشن کو محفوظ کرنے کے لیے منتقل کرتے ہیں، تو گروپ_x_صارف ٹیبل کے ذریعے ایک کنکشن بھی بن جائے گا۔ ایسی صورت حال ہو سکتی ہے جب اس طرح کا سبسکرپشن گروپ پہلے ہی بن چکا ہو اور آپ کو اس میں کسی اور صارف کو شامل کرنے کی ضرورت ہو۔ ایسا کرنے کے لیے، ہم سب سے پہلے ڈیٹا بیس سے گروپ آئی ڈی حاصل کرتے ہیں، اور اگر کوئی ریکارڈ موجود ہے تو ہم اس کے ساتھ کام کرتے ہیں، اگر نہیں، تو ہم ایک نیا بناتے ہیں۔ یہ نوٹ کرنا ضروری ہے کہ TelegramUser کے ساتھ کام کرنے کے لیے ہم TelegramUserService کا استعمال آخری ٹھوس اصولوں پر عمل کرتے ہیں۔ اس وقت، اگر ہمیں ID کے ذریعے کوئی ریکارڈ نہیں ملتا ہے، تو میں صرف ایک استثنا دیتا ہوں۔ اب اس پر کسی بھی طرح سے کارروائی نہیں کی جا رہی ہے: ہم MVP سے پہلے یہ بالکل آخر میں کریں گے۔ آئیے GroupSubServiceTest کلاس کے لیے دو یونٹ ٹیسٹ لکھتے ہیں ۔ ہمیں جن کی ضرورت ہے؟ میں اس بات کو یقینی بنانا چاہتا ہوں کہ محفوظ کرنے کا طریقہ GroupSubRepository میں بلایا جائے گا اور ایک واحد صارف کے ساتھ ایک ادارہ GroupSub کو بھیج دیا جائے گا - وہ جو فراہم کردہ ID کا استعمال کرتے ہوئے ہمیں TelegramUserService واپس کرے گا۔ اور دوسرا آپشن، جب ایک ہی ID والا گروپ ڈیٹا بیس میں پہلے سے موجود ہو اور اس گروپ میں پہلے سے ہی ایک صارف موجود ہو، اور آپ کو یہ چیک کرنا ہوگا کہ اس گروپ میں کوئی دوسرا صارف شامل ہو جائے گا اور یہ اعتراض محفوظ ہو جائے گا۔ یہاں عمل درآمد ہے:
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);
   }

}
میں نے BeforeEach تشریح کے ساتھ init() طریقہ بھی شامل کیا ۔ اس طرح، وہ عام طور پر ایک طریقہ بناتے ہیں جو ہر ٹیسٹ کے چلنے سے پہلے عمل میں لایا جائے گا، اور تمام ٹیسٹوں کے لیے اس میں عام منطق ڈالنا ممکن ہے۔ ہمارے معاملے میں، ہمیں اس کلاس کے تمام ٹیسٹوں کے لیے اسی طرح سے TelegramUserService کو لاک کرنے کی ضرورت ہے، اس لیے اس منطق کو ایک عام طریقہ میں منتقل کرنا سمجھ میں آتا ہے۔ یہاں دو موکیٹو ڈیزائن استعمال کیے گئے ہیں:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - اس میں ہم کہتے ہیں کہ جب طریقہ m1 کو آبجیکٹ o1 پر دلیل a1 کے ساتھ بلایا جاتا ہے تو طریقہ o2 کو واپس کرے گا ۔ یہ موکیٹو کی تقریباً سب سے اہم فعالیت ہے - فرضی آبجیکٹ کو بالکل وہی واپس کرنے پر مجبور کرنا جس کی ہمیں ضرورت ہے۔

  • Mockito.verify(o1).m1(a1) - جو اس طریقہ کی تصدیق کرتا ہے کہ m1 کو اعتراض o1 پر دلیل a1 کے ساتھ بلایا گیا تھا ۔ یقیناً محفوظ کرنے کے طریقہ کار کی واپسی آبجیکٹ کو استعمال کرنا ممکن تھا، لیکن میں نے ایک اور ممکنہ طریقہ دکھا کر اسے قدرے پیچیدہ بنانے کا فیصلہ کیا۔ یہ کب مفید ہو سکتا ہے؟ ایسی صورتوں میں جہاں فرضی کلاسوں کے طریقے باطل ہو جاتے ہیں۔ پھر Mockito.verify کے بغیر کوئی کام نہیں ہوگا)))

ہم اس خیال پر قائم رہتے ہیں کہ ٹیسٹ لکھنے کی ضرورت ہے، اور ان میں سے بہت کچھ لکھنے کی ضرورت ہے۔ اگلا مرحلہ ٹیلیگرام بوٹ ٹیم کے ساتھ کام کر رہا ہے۔

کمانڈ بنائیں /addGroupSub

یہاں ہمیں مندرجہ ذیل منطق کو انجام دینے کی ضرورت ہے: اگر ہمیں کسی سیاق و سباق کے بغیر صرف ایک کمانڈ موصول ہوتا ہے، تو ہم صارف کی مدد کرتے ہیں اور اسے ان کی IDs کے ساتھ تمام گروپس کی فہرست دیتے ہیں تاکہ وہ ضروری معلومات بوٹ تک پہنچا سکے۔ اور اگر صارف بوٹ کو کسی دوسرے لفظ کے ساتھ کمانڈ بھیجتا ہے، تو اس ID کے ساتھ ایک گروپ تلاش کریں یا لکھیں کہ ایسا کوئی گروپ نہیں ہے۔ آئیے اپنے نام میں ایک نئی قدر شامل کریں - CommandName:
ADD_GROUP_SUB("/addgroupsub")
آئیے ڈیٹا بیس سے ٹیلیگرام بوٹ کی طرف آگے بڑھتے ہیں - کمانڈ پیکج میں AddGroupSubCommand کلاس بنائیں:
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 = "Whatбы подписаться на группу - передай комадну вместе с ID группы. \n" +
               "Например: /addGroupSub 16. \n\n" +
               "я подготовил список всех групп - выберай Howую хочешь :) \n\n" +
               "Name группы - ID группы \n\n" +
               "%s";

       sendBotMessageService.sendMessage(chatId, String.format(message, groupIds));
   }
}
یہ کلاس اپاچی کامنز لائبریری سے isNumeric طریقہ استعمال کرتی ہے، تو آئیے اسے اپنی میموری میں شامل کریں:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
اور پراپرٹیز بلاک میں:
<apache.commons.version>3.11</apache.commons.version>
یہ ساری منطق کلاس میں ہے۔ اسے غور سے پڑھیں۔ اگر آپ کے کوئی سوالات/مشورے ہیں تو انہیں تبصروں میں لکھیں۔ اس کے بعد، ہمیں اپنے کمانڈ میپ میں کمانڈ کنٹینر میں کمانڈ شامل کرنے کی ضرورت ہے:
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
اور اس ٹیم کے لیے سب کچھ۔ میں کسی نہ کسی طرح اس فعالیت کی جانچ کرنا چاہوں گا، لیکن اب تک میں اسے صرف ڈیٹا بیس میں ہی دیکھ سکتا ہوں۔ حصہ تین میں، میں JRTB-6 سے تبدیلیاں شامل کروں گا تاکہ ہم ان گروپس کی فہرست دیکھ سکیں جن کے صارف نے سبسکرائب کیا ہے۔ اب یہ سب چیک کر لیا جائے تو اچھا ہو گا۔ ایسا کرنے کے لیے، ہم ٹیلیگرام میں تمام اعمال انجام دیں گے اور ڈیٹا بیس میں چیک کریں گے۔ چونکہ ہمارے پاس تحریری امتحانات ہیں، سب کچھ ٹھیک ہونا چاہیے۔ مضمون پہلے ہی کافی لمبا ہے، اس لیے ہم بعد میں AddGroupSubCommand کے لیے ایک ٹیسٹ لکھیں گے، اور کوڈ میں TODO شامل کریں گے تاکہ بھول نہ جائیں۔

نتائج

اس آرٹیکل میں، ہم نے ڈیٹا بیس سے شروع ہونے والے اور بوٹ استعمال کرنے والے کلائنٹ کے ساتھ کام کرنے پر ختم ہونے والی پوری ایپلیکیشن کے ذریعے فعالیت کو شامل کرنے کے کام کو دیکھا۔ عام طور پر اس طرح کے کام منصوبے کو سمجھنے اور اس کے جوہر کو سمجھنے میں مدد کرتے ہیں۔ سمجھیں کہ یہ کیسے کام کرتا ہے۔ ان دنوں موضوعات آسان نہیں ہیں، لہٰذا شرمندہ نہ ہوں: اپنے سوالات کمنٹس میں لکھیں، اور میں ان کا جواب دینے کی کوشش کروں گا۔ کیا آپ کو پروجیکٹ پسند ہے؟ اسے Github پر ایک ستارہ دیں : اس طرح یہ واضح ہو جائے گا کہ وہ اس منصوبے میں دلچسپی رکھتے ہیں، اور مجھے خوشی ہوگی۔ جیسا کہ وہ کہتے ہیں، ایک ماسٹر ہمیشہ خوش ہوتا ہے جب اس کے کام کی تعریف کی جاتی ہے. کوڈ STEP_6 کے تینوں حصوں پر مشتمل ہوگا اور اس مضمون سے پہلے دستیاب ہوگا۔ اس کے بارے میں کیسے معلوم کریں؟ یہ آسان ہے - ٹیلیگرام چینل میں شامل ہوں ، جہاں میں ٹیلیگرام بوٹ کے بارے میں اپنے مضامین کے بارے میں تمام معلومات شائع کرتا ہوں۔ پڑھنے کا شکریہ! حصہ 3 پہلے ہی یہاں ہے ۔

سیریز کے تمام مواد کی فہرست اس مضمون کے شروع میں ہے۔

تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION