Групповые уведомления о пайплайнах

This commit is contained in:
Struchkov Mark 2024-09-03 23:21:17 +03:00
parent 936490de23
commit e5727f2b30
No known key found for this signature in database
GPG Key ID: A3F0AC3F0FA52F3C
12 changed files with 197 additions and 46 deletions

View File

@ -21,7 +21,7 @@ public enum PipelineStatus {
SKIPPED("\uD83D\uDD18"),
MANUAL("\uD83D\uDD79"),
SCHEDULED("\uD83D\uDD52"),
NULL("\uD83C\uDD95");
NEW("\uD83C\uDD95");
private final String icon;

View File

@ -8,11 +8,13 @@ import lombok.Getter;
import java.util.Map;
import static dev.struchkov.bot.gitlab.context.domain.notify.group.mr.NewMergeRequestGroupPersonalNotifyFields.CLASS_NAME;
@Getter
@FieldNames
public class NewMergeRequestGroupPersonalNotify implements GroupNotify {
public static final String TYPE = "NewMergeRequestGroupNotify";
public static final String TYPE = CLASS_NAME;
protected final Long mrId;
protected final String projectName;

View File

@ -0,0 +1,37 @@
package dev.struchkov.bot.gitlab.context.domain.notify.group.pipeline;
import dev.struchkov.bot.gitlab.context.domain.PipelineStatus;
import dev.struchkov.bot.gitlab.context.domain.notify.GroupNotify;
import dev.struchkov.haiti.utils.fieldconstants.annotation.FieldNames;
import lombok.Builder;
import lombok.Getter;
@Getter
@FieldNames
public class PipelineGroupNotify implements GroupNotify {
public static final String TYPE = "PipelineGroupNotify";
private final String projectName;
private final String refName;
private final PipelineStatus oldStatus;
private final PipelineStatus newStatus;
private final String webUrl;
private final String ownerTelegramUsername;
@Builder
public PipelineGroupNotify(String projectName, String refName, PipelineStatus oldStatus, PipelineStatus newStatus, String webUrl, String ownerTelegramUsername) {
this.projectName = projectName;
this.refName = refName;
this.oldStatus = oldStatus;
this.newStatus = newStatus;
this.webUrl = webUrl;
this.ownerTelegramUsername = ownerTelegramUsername;
}
@Override
public String getType() {
return TYPE;
}
}

View File

@ -18,8 +18,7 @@ public final class PipelinePersonalNotify implements PersonalNotify {
public static final String TYPE = CLASS_NAME;
private final Long projectId;
private final Long pipelineId;
private final String projectName;
private final String refName;
private final PipelineStatus oldStatus;
private final PipelineStatus newStatus;
@ -27,15 +26,13 @@ public final class PipelinePersonalNotify implements PersonalNotify {
@Builder
public PipelinePersonalNotify(
Long projectId,
Long pipelineId,
String projectName,
String refName,
PipelineStatus oldStatus,
PipelineStatus newStatus,
String webUrl
) {
this.projectId = projectId;
this.pipelineId = pipelineId;
this.projectName = projectName;
this.refName = refName;
this.oldStatus = oldStatus;
this.newStatus = newStatus;

View File

@ -18,6 +18,8 @@ public interface PersonRepository {
List<Person> findAllById(Set<Long> personIds);
List<PersonTelegram> getAllTelegramInfoByIds(Set<Long> personIds);
Optional<PersonTelegram> findTelegramInfoById(Long personId);
List<PersonTelegram> findAllTelegramInfoByIds(Set<Long> personIds);
}

View File

@ -6,6 +6,7 @@ import lombok.NonNull;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
@ -21,6 +22,8 @@ public interface PersonService {
List<Person> createAll(List<Person> newPersons);
Optional<String> getTelegramUsernamesByPersonIds(Long personId);
Map<Long, String> getTelegramUsernamesByPersonIds(Set<Long> personIds);
}

View File

@ -6,8 +6,12 @@ import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.bot.gitlab.context.domain.entity.Pipeline;
import dev.struchkov.bot.gitlab.context.domain.event.NewPipelineEvent;
import dev.struchkov.bot.gitlab.context.domain.event.UpdatePipelineEvent;
import dev.struchkov.bot.gitlab.context.domain.notify.group.pipeline.PipelineGroupNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.pipeline.PipelinePersonalNotify;
import dev.struchkov.bot.gitlab.context.service.NotifyService;
import dev.struchkov.bot.gitlab.context.service.PersonService;
import dev.struchkov.bot.gitlab.context.service.ProjectService;
import dev.struchkov.haiti.utils.Strings;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
@ -15,6 +19,7 @@ import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import static dev.struchkov.bot.gitlab.context.domain.PipelineStatus.CANCELED;
@ -30,24 +35,48 @@ public class PipelineHandler {
// Статусы пайплайнов, о которых нужно уведомить
private static final Set<PipelineStatus> notificationStatus = Set.of(FAILED, SUCCESS, CANCELED, SKIPPED);
private final PersonService personService;
private final PersonInformation personInformation;
private final NotifyService notifyService;
private final ProjectService projectService;
@EventListener
public void newPipelineHandle(NewPipelineEvent event) {
final Pipeline pipeline = event.getPipeline();
if (isNeedNotifyNewPipeline(pipeline)) {
notifyService.send(
PipelinePersonalNotify.builder()
.projectId(pipeline.getProjectId())
final Optional<String> optProjectName = projectService.getProjectNameById(pipeline.getProjectId())
.map(Strings::escapeMarkdown);
if (isNeedPersonalNotifyPipeline(pipeline)) {
final PipelinePersonalNotify.PipelinePersonalNotifyBuilder builder = PipelinePersonalNotify.builder()
.newStatus(pipeline.getStatus())
.pipelineId(pipeline.getId())
.refName(pipeline.getRef())
.webUrl(pipeline.getWebUrl())
.oldStatus(PipelineStatus.NULL)
.build()
);
.oldStatus(PipelineStatus.NEW);
optProjectName.ifPresent(builder::projectName);
notifyService.send(builder.build());
}
if (isNeedPersonalGroupPipeline(pipeline)) {
final Optional<String> optTelegramUsername = personService.getTelegramUsernamesByPersonIds(pipeline.getPerson().getId());
if (optTelegramUsername.isPresent()) {
final String telegramUsername = optTelegramUsername.get();
final PipelineGroupNotify.PipelineGroupNotifyBuilder builder = PipelineGroupNotify.builder()
.newStatus(pipeline.getStatus())
.refName(pipeline.getRef())
.webUrl(pipeline.getWebUrl())
.oldStatus(PipelineStatus.NEW)
.ownerTelegramUsername(telegramUsername);
optProjectName.ifPresent(builder::projectName);
notifyService.send(builder.build());
}
}
}
@EventListener
@ -55,22 +84,43 @@ public class PipelineHandler {
final Pipeline oldPipeline = event.getOldPipeline();
final Pipeline newPipeline = event.getNewPipeline();
if (!Objects.equals(oldPipeline.getUpdated(), newPipeline.getUpdated())) {
if (isNeedNotifyNewPipeline(newPipeline)) {
notifyService.send(
PipelinePersonalNotify.builder()
.projectId(newPipeline.getProjectId())
final Optional<String> optProjectName = projectService.getProjectNameById(newPipeline.getProjectId())
.map(Strings::escapeMarkdown);
if (isNeedPersonalNotifyPipeline(newPipeline)) {
final PipelinePersonalNotify.PipelinePersonalNotifyBuilder builder = PipelinePersonalNotify.builder()
.newStatus(newPipeline.getStatus())
.refName(newPipeline.getRef())
.webUrl(newPipeline.getWebUrl())
.oldStatus(oldPipeline.getStatus());
optProjectName.ifPresent(builder::projectName);
notifyService.send(builder.build());
}
if (isNeedPersonalGroupPipeline(newPipeline)) {
final Optional<String> optTelegramUsername = personService.getTelegramUsernamesByPersonIds(newPipeline.getPerson().getId());
if (optTelegramUsername.isEmpty()) {
final String telegramUsername = optTelegramUsername.get();
final PipelineGroupNotify.PipelineGroupNotifyBuilder builder = PipelineGroupNotify.builder()
.newStatus(newPipeline.getStatus())
.pipelineId(newPipeline.getId())
.refName(newPipeline.getRef())
.webUrl(newPipeline.getWebUrl())
.oldStatus(oldPipeline.getStatus())
.build()
);
}
.ownerTelegramUsername(telegramUsername);
optProjectName.ifPresent(builder::projectName);
notifyService.send(builder.build());
}
}
private boolean isNeedNotifyNewPipeline(@NonNull Pipeline pipeline) {
}
}
private boolean isNeedPersonalNotifyPipeline(@NonNull Pipeline pipeline) {
final Person personPipelineCreator = pipeline.getPerson();
return notificationStatus.contains(pipeline.getStatus()) // Пайплайн имеет статус необходимый для уведомления
&& checkNotNull(personPipelineCreator) // Создатель пайплайна не null
@ -78,4 +128,11 @@ public class PipelineHandler {
&& LocalDateTime.now().minusDays(1).isBefore(pipeline.getCreated()); // Пайплан был создан не более 24 часов назад
}
private boolean isNeedPersonalGroupPipeline(@NonNull Pipeline pipeline) {
final Person personPipelineCreator = pipeline.getPerson();
return notificationStatus.contains(pipeline.getStatus()) // Пайплайн имеет статус необходимый для уведомления
&& checkNotNull(personPipelineCreator) // Создатель пайплайна не null
&& LocalDateTime.now().minusDays(1).isBefore(pipeline.getCreated()); // Пайплан был создан не более 24 часов назад
}
}

View File

@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@ -61,9 +62,14 @@ public class PersonServiceImpl implements PersonService {
.toList();
}
@Override
public Optional<String> getTelegramUsernamesByPersonIds(Long personId) {
return repository.findTelegramInfoById(personId).map(PersonTelegram::getTelegramUserName);
}
@Override
public Map<Long, String> getTelegramUsernamesByPersonIds(Set<Long> personIds) {
return repository.getAllTelegramInfoByIds(personIds).stream()
return repository.findAllTelegramInfoByIds(personIds).stream()
.collect(Collectors.toMap(PersonTelegram::getId, PersonTelegram::getTelegramUserName));
}

View File

@ -38,7 +38,12 @@ public class PersonRepositoryImpl implements PersonRepository {
}
@Override
public List<PersonTelegram> getAllTelegramInfoByIds(Set<Long> personIds) {
public Optional<PersonTelegram> findTelegramInfoById(Long personId) {
return jpaTelegramRepository.findById(personId);
}
@Override
public List<PersonTelegram> findAllTelegramInfoByIds(Set<Long> personIds) {
return jpaTelegramRepository.findAllById(personIds);
}

View File

@ -8,4 +8,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
*/
public interface PersonJpaRepository extends JpaRepository<Person, Long> {
}

View File

@ -1,41 +1,33 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.notify.pipeline.PipelinePersonalNotify;
import dev.struchkov.bot.gitlab.context.service.ProjectService;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.simple.domain.BoxAnswer;
import dev.struchkov.haiti.utils.Strings;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
import static dev.struchkov.bot.gitlab.telegram.utils.UnitName.DELETE_MESSAGE;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
import static dev.struchkov.godfather.telegram.domain.keyboard.SimpleKeyBoardLine.keyBoardLine;
import static dev.struchkov.godfather.telegram.domain.keyboard.button.SimpleButton.simpleButton;
import static dev.struchkov.godfather.telegram.domain.keyboard.button.UrlButton.urlButton;
import static dev.struchkov.godfather.telegram.main.context.BoxAnswerPayload.ENABLE_MARKDOWN;
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
@Service
@RequiredArgsConstructor
public class PipelineNotifyGenerator implements NotifyBoxAnswerGenerator<PipelinePersonalNotify> {
private final ProjectService projectService;
@Override
public BoxAnswer generate(PipelinePersonalNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.BUILD).append(" *New pipeline | ").append(notify.getPipelineId()).append("*");
final StringBuilder builder = new StringBuilder(Icons.BUILD).append(" *New pipeline* ");
builder
.append(Icons.HR);
final Optional<String> optProjectName = projectService.getProjectNameById(notify.getProjectId())
.map(Strings::escapeMarkdown);
if (optProjectName.isPresent()) {
final String projectName = optProjectName.get();
builder.append(Icons.PROJECT).append(": ").append(projectName).append("\n");
if (checkNotEmpty(notify.getProjectName())) {
builder.append(Icons.PROJECT).append(": ").append(notify.getProjectName()).append("\n");
}
builder

View File

@ -0,0 +1,49 @@
package dev.struchkov.bot.gitlab.telegram.service.notify.group;
import dev.struchkov.bot.gitlab.context.domain.notify.group.pipeline.PipelineGroupNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.bot.gitlab.telegram.service.notify.NotifyBoxAnswerGenerator;
import dev.struchkov.godfather.simple.domain.BoxAnswer;
import org.springframework.stereotype.Component;
import static dev.struchkov.godfather.telegram.domain.keyboard.InlineKeyBoard.inlineKeyBoard;
import static dev.struchkov.godfather.telegram.domain.keyboard.SimpleKeyBoardLine.keyBoardLine;
import static dev.struchkov.godfather.telegram.domain.keyboard.button.UrlButton.urlButton;
import static dev.struchkov.godfather.telegram.main.context.BoxAnswerPayload.ENABLE_MARKDOWN;
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
@Component
public class PipelineGroupNotifyGenerator implements NotifyBoxAnswerGenerator<PipelineGroupNotify> {
@Override
public BoxAnswer generate(PipelineGroupNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.BUILD).append(" ").append(notify.getOwnerTelegramUsername()).append(" *you started a new pipeline*")
.append(Icons.HR);
if (checkNotEmpty(notify.getProjectName())) {
builder.append(Icons.PROJECT).append(": ").append(notify.getProjectName()).append("\n");
}
builder
.append(Icons.TREE).append(": ").append(notify.getRefName())
.append(Icons.HR)
.append(notify.getOldStatus().getIcon()).append(" ").append(notify.getOldStatus()).append(Icons.ARROW).append(notify.getNewStatus().getIcon()).append(" ").append(notify.getNewStatus());
return BoxAnswer.builder()
.message(builder.toString())
.keyBoard(inlineKeyBoard(
keyBoardLine(
urlButton(Icons.LINK, notify.getWebUrl())
)
))
.payload(ENABLE_MARKDOWN)
.build();
}
@Override
public String getNotifyType() {
return PipelineGroupNotify.TYPE;
}
}