Hamıya salam. Xatırladım: birinci hissədə Flyway əlavə etdik. Davam edək.
docker-compose.yml-ə verilənlər bazası əlavə edilməsi
Növbəti mərhələ əsas docker-compose.yml-də verilənlər bazası ilə işin qurulmasıdır. Gəlin verilənlər bazasını docker-compose faylına əlavə edək:version: '3.1'
services:
jrtb-bot:
depends_on:
- jrtb-db
build:
context: .
environment:
BOT_NAME: ${BOT_NAME}
BOT_TOKEN: ${BOT_TOKEN}
BOT_DB_USERNAME: ${BOT_DB_USERNAME}
BOT_DB_PASSWORD: ${BOT_DB_PASSWORD}
restart: always
jrtb-db:
image: mysql:5.7
restart: always
environment:
MYSQL_USER: ${BOT_DB_USERNAME}
MYSQL_PASSWORD: ${BOT_DB_PASSWORD}
MYSQL_DATABASE: 'jrtb_db'
MYSQL_ROOT_PASSWORD: 'root'
ports:
- '3306:3306'
expose:
- '3306'
Mən də ərizəmizə bu xətti əlavə etdim:
depends_on:
- jrtb-db
Bu o deməkdir ki, biz proqrama başlamazdan əvvəl verilənlər bazasının başlamasını gözləyirik. Sonra, verilənlər bazası ilə işləmək üçün lazım olan daha iki dəyişənin əlavə edildiyini görə bilərsiniz:
${BOT_DB_USERNAME}
${BOT_DB_PASSWORD}
Biz onları docker-compose-də teleqram botu ilə eyni şəkildə - mühit dəyişənləri vasitəsilə əldə edəcəyik. Mən bunu elə etdim ki, verilənlər bazası istifadəçi adı və parolunun dəyərlərini təyin etdiyimiz yalnız bir yerimiz olsun. Biz onları tətbiqimizin docker şəklinə və verilənlər bazamızın docker konteynerinə ötürürük. Sonra SpringBoot-a verilənlər bazası üçün dəyişənləri qəbul etməyi öyrətmək üçün Dockerfile-ni yeniləmək lazımdır.
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
ENV BOT_NAME=test.javarush_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}", "-Dspring.datasource.username=${BOT_DB_USERNAME}", "-jar", "app.jar"]
İndi biz Dockerfile verilənlər bazası dəyişənlərini əlavə edirik:
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
Dəyişən dəyərlər fərqli olacaq. Dockerfile-ə keçəcəyimizlər standart dəyərlər tələb edir, ona görə də bəzilərini daxil etdim. Son sətri iki elementlə genişləndiririk, onların köməyi ilə DB istifadəçi adı və şifrəni tətbiqin işə salınmasına keçirəcəyik:
"-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}"
Dockerfile-dəki son sətir (ENTRYPOINT ilə başlayır) bükülmədən olmalıdır. Əgər köçürmə etsəniz, bu kod işləməyəcək. Son addım dəyişənləri verilənlər bazasına ötürmək üçün start.sh faylını yeniləməkdir .
#!/bin/bash
# Pull new changes
git pull
# Prepare Jar
mvn clean
mvn package
# Ensure, that docker-compose stopped
docker-compose stop
# Add environment variables
export BOT_NAME=$1
export BOT_TOKEN=$2
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
# Start new deployment
docker-compose up --build -d
Docker-compose-ni işə salmazdan əvvəl ətraf mühit dəyişənlərini necə əlavə edəcəyimizi artıq bilirik. Bunun üçün sadəcə var_name=var_value ixracını yerinə yetirmək lazımdır. Buna görə də biz yalnız iki sətir əlavə edirik:
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
Bu, verilənlər bazası istifadəçi adı və şifrəni təyin etdiyimiz yerdir. Əlbəttə ki, botun adı və işarəsi üçün etdiyimiz kimi, bash skriptini işləyərkən bu dəyişənləri ötürmək mümkün olardı. Amma mənə elə gəlir ki, bu, lazımsızdır. Verilənlər bazasına faktiki olaraq daxil olmaq üçün verilənlər bazasının yerləşdiriləcəyi serverin IP-ni bilməli və sorğu üçün icazə verilən IP ünvanları siyahısında olmalısınız. Mənə gəlincə, bu artıq kifayətdir. Əsası qoyuldu: indi siz tərtibatçı üçün daha başa düşülən şeylər edə bilərsiniz - kod yaza bilərsiniz. Bundan əvvəl biz DevOps mühəndislərinin gördüyü işlərlə məşğul idik - ətraf mühitin qurulması.
Repozitor qatının əlavə edilməsi
Tipik olaraq bir tətbiq üç qatdan ibarətdir:- Nəzarətçilər proqrama giriş nöqtələridir.
- Xidmətlər biznes məntiqinin işlədiyi yerdir. Bizdə artıq qismən bu var: SendMessageService biznes məntiqinin açıq nümayəndəsidir.
- Repozitoriyalar verilənlər bazası ilə işləmək üçün yerdir. Bizim vəziyyətimizdə bu teleqram botudur.
- Biz JDBC ilə işləmək məcburiyyətində qalmayacağıq: biz birbaşa daha yüksək abstraksiyalarla işləyəcəyik. Yəni verilənlər bazasında cədvəllərə uyğun gələn POJO-ları saxlayın. Biz bu cür sinifləri obyekt adlandıracağıq , çünki onlar rəsmi olaraq Java Persistence API- də adlanır (bu, ORM vasitəsilə verilənlər bazası ilə işləmək üçün ümumi interfeyslər dəstidir, yəni JDBC ilə işləmək üzərində abstraksiyadır). Verilənlər bazasında saxlayacağımız bir varlıq sinfimiz olacaq və onlar tam olaraq bizə lazım olan cədvələ yazılacaq. Verilənlər bazasında axtarış edərkən eyni obyektləri alacağıq.
- Spring Data onların interfeys dəstindən istifadə etməyi təklif edir: JpaRepository , CrudRepository , və s... Başqa interfeyslər də var: tam siyahını burada tapa bilərsiniz . Gözəlliyi ondadır ki, onların üsullarını həyata keçirmədən də istifadə edə bilərsiniz(!). Üstəlik, interfeysdə yeni metodlar yaza biləcəyiniz müəyyən bir şablon var və onlar avtomatik olaraq həyata keçiriləcək.
- Bahar inkişafımızı bacardığı qədər asanlaşdırır. Bunun üçün öz interfeysimizi yaratmalı və yuxarıda təsvir edilənlərdən miras almalıyıq. Baharın bu interfeysdən istifadə etməsi lazım olduğunu bilməsi üçün Repository annotasiyasını əlavə edin.
- Əgər mövcud olmayan verilənlər bazası ilə işləmək üçün metod yazmaq lazımdırsa, bu da problem deyil - biz onu yazacağıq. Orada nə və necə edəcəyinizi sizə göstərəcəyəm.
package com.github.javarushcommunity.jrtb.repository.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* Telegram User entity.
*/
@Data
@Entity
@Table(name = "tg_user")
public class TelegramUser {
@Id
@Column(name = "chat_id")
private String chatId;
@Column(name = "active")
private boolean active;
}
Burada javax.persistence paketindən bütün annotasiyaların bizdə olduğunu görə bilərsiniz. Bunlar bütün ORM tətbiqləri üçün istifadə olunan ümumi qeydlərdir. Varsayılan olaraq, Spring Data Jpa Hibernate-dən istifadə edir, baxmayaraq ki, digər tətbiqlər istifadə edilə bilər. İstifadə etdiyimiz annotasiyaların siyahısı budur:
- Müəssisə - bunun verilənlər bazası ilə işləmək üçün bir qurum olduğunu göstərir;
- Cədvəl - burada cədvəlin adını müəyyən edirik;
- Id - annotasiya cədvəldə hansı sahənin İlkin Açar olacağını bildirir;
- Sütun - cədvəldən sahənin adını müəyyənləşdirin.
package com.github.javarushcommunity.jrtb.repository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* {@link Repository} for handling with {@link TelegramUser} entity.
*/
@Repository
public interface TelegramUserRepository extends JpaRepository<TelegramUser, String> {
List<TelegramUser> findAllByActiveTrue();
}
Burada heç yerdə tətbiq etmədiyim findAllByActiveTrue() metodunu necə əlavə etdiyimi görə bilərsiniz . Amma bu, onun işləməsinə mane olmayacaq. Spring Data , aktiv sahəsi = true olan tg_user cədvəlindən bütün qeydləri əldə etməli olduğunu başa düşəcəkdir . Biz TelegramUser obyekti ilə işləmək üçün xidmət əlavə edirik (biz SOLID-dən asılılığın inversiyasından o kontekstdə istifadə edirik ki, digər qurumların xidmətləri başqa bir qurumun repozitoriyası ilə birbaşa əlaqə saxlaya bilməz - yalnız həmin qurumun xidməti vasitəsilə). Biz paketdə TelegramUserService xidmətini yaradırıq, onun hələlik bir neçə üsulu olacaq: istifadəçini yadda saxla, istifadəçini şəxsiyyət vəsiqəsi ilə əldə et və aktiv istifadəçilərin siyahısını göstər. Əvvəlcə TelegramUserService interfeysini yaradırıq:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* {@link Service} for handling {@link TelegramUser} entity.
*/
public interface TelegramUserService {
/**
* Save provided {@link TelegramUser} entity.
*
* @param telegramUser provided telegram user.
*/
void save(TelegramUser telegramUser);
/**
* Retrieve all active {@link TelegramUser}.
*
* @return the collection of the active {@link TelegramUser} objects.
*/
List<TelegramUser> retrieveAllActiveUsers();
/**
* Find {@link TelegramUser} by chatId.
*
* @param chatId provided Chat ID
* @return {@link TelegramUser} with provided chat ID or null otherwise.
*/
Optional<TelegramUser> findByChatId(String chatId);
}
Və əslində TelegramUserServiceImpl tətbiqi:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.repository.TelegramUserRepository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* Implementation of {@link TelegramUserService}.
*/
@Service
public class TelegramUserServiceImpl implements TelegramUserService {
private final TelegramUserRepository telegramUserRepository;
@Autowired
public TelegramUserServiceImpl(TelegramUserRepository telegramUserRepository) {
this.telegramUserRepository = telegramUserRepository;
}
@Override
public void save(TelegramUser telegramUser) {
telegramUserRepository.save(telegramUser);
}
@Override
public List<TelegramUser> retrieveAllActiveUsers() {
return telegramUserRepository.findAllByActiveTrue();
}
@Override
public Optional<TelegramUser> findByChatId(String chatId) {
return telegramUserRepository.findById(chatId);
}
}
Burada qeyd etmək lazımdır ki, biz Autowired annotasiyasından istifadə edərək TelegramuserRepository obyektinin asılılıq inyeksiyasından (sinif nümunəsini təqdim edirik) və konstruktordan istifadə edirik. Siz bunu dəyişən üçün edə bilərsiniz, lakin bu, Spring Framework komandasının bizə tövsiyə etdiyi yanaşmadır.
Bot üçün statistik məlumatların əlavə edilməsi
Sonra /start və /stop əmrlərini yeniləməlisiniz. /start əmrindən istifadə edildikdə, yeni istifadəçini verilənlər bazasında saxlamaq və onu aktiv = doğru olaraq təyin etmək lazımdır. Və /stop olduqda, istifadəçi məlumatlarını yeniləyin: aktiv = false təyin edin. StartCommand sinifini düzəldək :package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;
/**
* Start {@link Command}.
*/
public class StartCommand implements Command {
private final SendBotMessageService sendBotMessageService;
private final TelegramUserService telegramUserService;
public final static String START_MESSAGE = "Привет. Я Javarush Telegram Bot. Я помогу тебе быть в курсе последних " +
"статей тех авторов, котрые тебе интересны. Я еще маленький и только учусь.";
public StartCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
String chatId = update.getMessage().getChatId().toString();
telegramUserService.findByChatId(chatId).ifPresentOrElse(
user -> {
user.setActive(true);
telegramUserService.save(user);
},
() -> {
TelegramUser telegramUser = new TelegramUser();
telegramUser.setActive(true);
telegramUser.setChatId(chatId);
telegramUserService.save(telegramUser);
});
sendBotMessageService.sendMessage(chatId, START_MESSAGE);
}
}
Burada həm də TelegramuserService obyektini konstruktora ötürürük, onunla yeni istifadəçini saxlayacağıq. Bundan əlavə, Java-da Optional-ın ləzzətlərindən istifadə edərək, aşağıdakı məntiq işləyir: verilənlər bazasında istifadəçi varsa, onu sadəcə aktivləşdiririk, əgər yoxdursa, yeni bir aktiv yaradırıq. Dayandır əmri:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;
import java.util.Optional;
/**
* Stop {@link Command}.
*/
public class StopCommand implements Command {
private final SendBotMessageService sendBotMessageService;
private final TelegramUserService telegramUserService;
public static final String STOP_MESSAGE = "Деактивировал все ваши подписки \uD83D\uDE1F.";
public StopCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), STOP_MESSAGE);
telegramUserService.findByChatId(update.getMessage().getChatId().toString())
.ifPresent(it -> {
it.setActive(false);
telegramUserService.save(it);
});
}
}
Eyni şəkildə TelegramServiceTest-i StopCommand-a keçirik. Əlavə məntiq belədir: əgər belə bir chat ID-si olan istifadəçimiz varsa, onu deaktiv edirik, yəni aktiv = false təyin edirik. Bunu öz gözlərinizlə necə görə bilərsiniz? Botun statistikasını göstərəcək yeni /stat əmri yaradaq. Bu mərhələdə bütün istifadəçilər üçün açıq olan sadə statistikalar olacaq. Gələcəkdə biz onu məhdudlaşdıracağıq və girişi yalnız idarəçilər üçün edəcəyik. Statistikada bir qeyd olacaq: aktiv bot istifadəçilərinin sayı. Bunu etmək üçün CommandName-ə STAT("/stat") dəyərini əlavə edin . Sonra StatCommand sinifini yaradın:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.telegram.telegrambots.meta.api.objects.Update;
/**
* Statistics {@link Command}.
*/
public class StatCommand implements Command {
private final TelegramUserService telegramUserService;
private final SendBotMessageService sendBotMessageService;
public final static String STAT_MESSAGE = "Javarush Telegram Bot использует %s человек.";
@Autowired
public StatCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
int activeUserCount = telegramUserService.retrieveAllActiveUsers().size();
sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), String.format(STAT_MESSAGE, activeUserCount));
}
}
Burada hər şey sadədir: biz retrieveAllActiveUsers metodundan istifadə edərək bütün aktiv istifadəçilərin siyahısını alırıq və kolleksiyanın ölçüsünü əldə edirik. Biz də indi artan sinifləri yeniləməliyik: CommandContainer və JavarushTelegramBot ki, onlar bizə lazım olan yeni xidməti keçməyi öyrənsinlər. Komanda Konteyneri:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import com.google.common.collect.ImmutableMap;
import static com.github.javarushcommunity.jrtb.command.CommandName.*;
/**
* Container of the {@link Command}s, which are using for handling telegram commands.
*/
public class CommandContainer {
private final ImmutableMap<String, Command> commandMap;
private final Command unknownCommand;
public CommandContainer(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
commandMap = ImmutableMap.<String, Command>builder()
.put(START.getCommandName(), new StartCommand(sendBotMessageService, telegramUserService))
.put(STOP.getCommandName(), new StopCommand(sendBotMessageService, telegramUserService))
.put(HELP.getCommandName(), new HelpCommand(sendBotMessageService))
.put(NO.getCommandName(), new NoCommand(sendBotMessageService))
.put(STAT.getCommandName(), new StatCommand(sendBotMessageService, telegramUserService))
.build();
unknownCommand = new UnknownCommand(sendBotMessageService);
}
public Command retrieveCommand(String commandIdentifier) {
return commandMap.getOrDefault(commandIdentifier, unknownCommand);
}
}
Burada xəritəyə yeni bir əmr əlavə etdik və onu TelegramUserService konstruktorundan keçirdik. Ancaq botun özündə yalnız konstruktor dəyişəcək:
@Autowired
public JavarushTelegramBot(TelegramUserService telegramUserService) {
this.commandContainer = new CommandContainer(new SendBotMessageServiceImpl(this), telegramUserService);
}
İndi biz Autowired annotasiyasını əlavə edərək, TelegramUserService-i arqument kimi təqdim edirik. Bu o deməkdir ki, biz onu Tətbiq Kontekstindən alacağıq. Biz həmçinin HelpCommand sinfini yeniləyəcəyik ki, təsvirdə yeni statistika əmri görünsün.
Manual test
Gəlin verilənlər bazasını docker-compose-test.yml-dən və JavarushTelegramBotApplication sinfində əsas metoddan işə salaq. Sonra bir sıra əmrlər yazırıq:- /stat - biz gözləyirik ki, verilənlər bazası boş olarsa, bu botdan istifadə edən sıfır insan olacaq;
- /start - botu işə salın;
- /stat - indi botun 1 nəfər tərəfindən istifadə ediləcəyini gözləyirik;
- /stop - botu dayandırmaq;
- /stat - biz gözləyirik ki, yenə ondan istifadə edən 0 nəfər olacaq.
Testlər yazırıq və yeniləyirik
Konstruktorları dəyişdirdiyimiz üçün test siniflərini də yeniləməli olacağıq. AbstractCommandTest sinfində daha bir sahə əlavə etməliyik - üç əmr üçün lazım olan TelegramUserService sinfi:protected TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
Sonra, CommandContainer-də init() metodunu yeniləyək :
@BeforeEach
public void init() {
SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
commandContainer = new CommandContainer(sendBotMessageService, telegramUserService);
}
StartCommand-da getCommand() metodunu yeniləməlisiniz :
@Override
Command getCommand() {
return new StartCommand(sendBotMessageService, telegramUserService);
}
Həmçinin StopCommand-da:
@Override
Command getCommand() {
return new StopCommand(sendBotMessageService, telegramUserService);
}
Sonra, yeni testlərə baxaq. StatCommand üçün tipik bir test yaradaq :
package com.github.javarushcommunity.jrtb.command;
import static com.github.javarushcommunity.jrtb.command.CommandName.STAT;
import static com.github.javarushcommunity.jrtb.command.StatCommand.STAT_MESSAGE;
public class StatCommandTest extends AbstractCommandTest {
@Override
String getCommandName() {
return STAT.getCommandName();
}
@Override
String getCommandMessage() {
return String.format(STAT_MESSAGE, 0);
}
@Override
Command getCommand() {
return new StatCommand(sendBotMessageService, telegramUserService);
}
}
Bu sadədir. İndi verilənlər bazası ilə işləməyi necə sınaqdan keçirəcəyimizdən danışaq. Əvvəllər etdiyimiz tək şey vahid testləri idi. İnteqrasiya testi tətbiqin çoxsaylı hissələri arasında inteqrasiyanı yoxlayır. Məsələn, proqramlar və verilənlər bazası. Burada hər şey daha mürəkkəb olacaq, çünki sınaq üçün bizə yerləşdirilən verilənlər bazası lazımdır. Buna görə də, testlərimizi yerli olaraq həyata keçirəndə, docker-compose-test.yml-dən işləyən verilənlər bazasına sahib olmalıyıq. Bu testi yerinə yetirmək üçün bütün SpringBoot tətbiqini işə salmalısınız. Test sinifində tətbiqi işə salacaq SpringBootTest annotasiyası var. Amma bu yanaşma bizim üçün işləməyəcək, çünki proqram işə salındıqda teleqram botu da işə düşəcək. Ancaq burada bir ziddiyyət var. Testlər həm yerli olaraq maşınımızda, həm də GitHub Actions vasitəsilə açıq şəkildə həyata keçiriləcək. Testlərin bütün tətbiqin işə salınması ilə keçməsi üçün biz onları teleqram botunda etibarlı məlumatlarla işlətməliyik: yəni adı və işarəsi ilə... Buna görə də bizim iki seçimimiz var:
- Odur ki, botun adını və nişanını ictimailəşdirin və ümid edin ki, hər şey yaxşı olacaq, heç kim ondan istifadə edib bizə müdaxilə etməyəcək.
- Başqa bir yol tapın.
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
Bizim üçün onlar ./src/test/resources/ + annotasiyada göstərilən yol boyunca yerləşəcəklər. Onlar necə görünür:
clearDbs.sql:
DELETE FROM tg_user;
telegram_users.sql:
INSERT INTO tg_user VALUES ("123456789", 1);
INSERT INTO tg_user VALUES ("123456788", 1);
INSERT INTO tg_user VALUES ("123456787", 1);
INSERT INTO tg_user VALUES ("123456786", 1);
INSERT INTO tg_user VALUES ("123456785", 1);
INSERT INTO tg_user VALUES ("123456784", 0);
INSERT INTO tg_user VALUES ("123456782", 0);
INSERT INTO tg_user VALUES ("123456781", 0);
Nəticədə TelegramUserRepositoryIT testimiz belə görünəcək (gördüyünüz kimi, inteqrasiya testinin adı fərqli olacaq - biz Testi yox, İT əlavə edirik):
package com.github.javarushcommunity.jrtb.repository;
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 TelegramUserRepository}.
*/
@ActiveProfiles("test")
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class TelegramUserRepositoryIT {
@Autowired
private TelegramUserRepository telegramUserRepository;
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
@Test
public void shouldProperlyFindAllActiveUsers() {
//when
List<TelegramUser> users = telegramUserRepository.findAllByActiveTrue();
//then
Assertions.assertEquals(5, users.size());
}
@Sql(scripts = {"/sql/clearDbs.sql"})
@Test
public void shouldProperlySaveTelegramUser() {
//given
TelegramUser telegramUser = new TelegramUser();
telegramUser.setChatId("1234567890");
telegramUser.setActive(false);
telegramUserRepository.save(telegramUser);
//when
Optional<TelegramUser> saved = telegramUserRepository.findById(telegramUser.getChatId());
//then
Assertions.assertTrue(saved.isPresent());
Assertions.assertEquals(telegramUser, saved.get());
}
}
Testləri yazdıq, amma sual yaranır: GitHub-da CI prosesimizin işə salınması ilə nə baş verəcək? Onun verilənlər bazası olmayacaq. Hələlik həqiqətən yalnız qırmızı bir quruluş olacaq. Bunu etmək üçün GitHub əməliyyatlarımız var ki, bu əməliyyatlarda biz quruluşumuzun işə salınmasını konfiqurasiya edə bilərik. Testləri işə salmazdan əvvəl, lazımi parametrlərlə verilənlər bazası işə salınmasını əlavə etməlisiniz. Göründüyü kimi, İnternetdə çoxlu nümunələr yoxdur, ona görə də sizə bunu bir yerdə saxlamağı məsləhət görürəm. .github/workflows/maven.yml faylını yeniləyək:
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Java CI with Maven
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up MySQL
uses: mirromutth/mysql-action@v1.1
with:
mysql version: '5.7'
mysql database: 'dev_jrtb_db'
mysql root password: 'root'
mysql user: 'dev_jrtb_db_user'
mysql password: 'dev_jrtb_db_password'
- name: Set up JDK 1.11
uses: actions/setup-java@v1
with:
java-version: 1.11
- name: Build with Maven
run: mvn -B package --file pom.xml
İndi yeni bir MySQL qurma bloku var . Biz burada ehtiyac duyduğumuz dəyişənləri eyni vaxtda təyin edərək, CI prosesimizə MySQL əlavə edirik. İndi istədiyimiz hər şeyi əlavə etdik. Son mərhələ dəyişiklikləri itələmək və quruluşun keçəcəyini və yaşıl olacağını görməkdir.
Sənədlərin yenilənməsi
Gəlin layihə versiyasını pom.xml-də 0.3.0-SNAPSHOT-dan 0.4.0-SNAPSHOT-a yeniləyək və həmçinin RELEASE_NOTES-ə əlavə edək:## 0.4.0-SNAPSHOT
* JRTB-1: added repository layer.
Bütün bunlardan sonra biz commit, push and pull sorğusu yaradırıq. Və ən əsası, quruluşumuz yaşıldır!
Faydalı bağlantılar:
- Telegram botumuzun anbarı
- Məqalədə təsvir edilən bütün dəyişikliklərlə sorğunu çəkin
- SpringBoot + Flyway məqaləsi
- DockerHub-dan MySQL şəkli
- Orta: Docker Compose ilə MySql Nümunəsini necə yaratmaq olar
- Habr: Spring Data Jpa
- Telegram kanalım
GO TO FULL VERSION