Добавлена поддержка апрувов

This commit is contained in:
Struchkov Mark 2024-08-26 20:21:30 +03:00
parent 214c0401ac
commit e844942973
No known key found for this signature in database
GPG Key ID: A3F0AC3F0FA52F3C
12 changed files with 253 additions and 35 deletions

View File

@ -0,0 +1,51 @@
package dev.struchkov.bot.gitlab.context.domain.changed;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ApprovalChanged {
private Set<Person> newApproval;
private Set<Person> dontApprove;
public static Optional<ApprovalChanged> approvalChanged(List<Person> oldApproval, List<Person> newApproval) {
// Если списки одинаковы, возвращаем пустой Optional
if (Objects.equals(oldApproval, newApproval)) {
return Optional.empty();
}
// Преобразуем списки в множества для удобства вычислений
Set<Person> oldApprovalSet = new HashSet<>(oldApproval);
Set<Person> newApprovalSet = new HashSet<>(newApproval);
// Вычисляем новые одобрения: те, кто есть в новом списке, но нет в старом
final Set<Person> newApprovalSetOnly = new HashSet<>(newApprovalSet);
newApprovalSetOnly.removeAll(oldApprovalSet);
// Вычисляем тех, кто больше не одобряет: те, кто есть в старом списке, но нет в новом
final Set<Person> dontApproveSet = new HashSet<>(oldApprovalSet);
dontApproveSet.removeAll(newApprovalSet);
if (!newApprovalSetOnly.isEmpty() || !dontApproveSet.isEmpty()) {
return Optional.of(new ApprovalChanged(newApprovalSetOnly, dontApproveSet));
}
return Optional.empty();
}
}

View File

@ -1,4 +1,4 @@
package dev.struchkov.bot.gitlab.context.domain;
package dev.struchkov.bot.gitlab.context.domain.changed;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import lombok.Getter;

View File

@ -1,4 +1,4 @@
package dev.struchkov.bot.gitlab.context.domain;
package dev.struchkov.bot.gitlab.context.domain.changed;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import lombok.Getter;

View File

@ -1,7 +1,8 @@
package dev.struchkov.bot.gitlab.context.domain.event;
import dev.struchkov.bot.gitlab.context.domain.AssigneeChanged;
import dev.struchkov.bot.gitlab.context.domain.ReviewerChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.ApprovalChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.AssigneeChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.ReviewerChanged;
import dev.struchkov.bot.gitlab.context.domain.entity.MergeRequest;
import lombok.AllArgsConstructor;
import lombok.Builder;
@ -9,6 +10,8 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Optional;
@Getter
@Setter
@Builder
@ -21,5 +24,6 @@ public class UpdateMergeRequestEvent {
private AssigneeChanged assigneeChanged;
private ReviewerChanged reviewerChanged;
private Optional<ApprovalChanged> optApprovalChanged;
}

View File

@ -0,0 +1,41 @@
package dev.struchkov.bot.gitlab.context.domain.notify.mergerequest;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.haiti.utils.fieldconstants.annotation.FieldNames;
import lombok.Builder;
import lombok.Getter;
import java.util.Set;
import static dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.ApprovalChangedMrNotifyFields.CLASS_NAME;
@Getter
@FieldNames
public class ApprovalChangedMrNotify extends MrNotify {
public static final String TYPE = CLASS_NAME;
private final Set<Person> newApproval;
private final Set<Person> dontApproval;
@Builder
public ApprovalChangedMrNotify(
Long mrId,
String projectName,
String title,
String url,
String milestone,
Set<Person> newApproval,
Set<Person> dontApproval
) {
super(mrId, projectName, title, url, milestone);
this.newApproval = newApproval;
this.dontApproval = dontApproval;
}
@Override
public String getType() {
return TYPE;
}
}

View File

@ -19,6 +19,7 @@ public class Icons {
public static final String GREEN_CIRCLE = "\uD83D\uDFE2";
public static final String PEN = "✏️";
public static final String ASSIGNEE = "\uD83C\uDFA9";
public static final String APPROVAL = "\uD83D\uDC4D";
public static final String BUILD = "⚙️";
public static final String LINK = "\uD83D\uDD17";
public static final String REVIEWER = "\uD83D\uDD0E";

View File

@ -1,15 +1,17 @@
package dev.struchkov.bot.gitlab.core.handler;
import dev.struchkov.bot.gitlab.context.domain.AssigneeChanged;
import dev.struchkov.bot.gitlab.context.domain.MergeRequestState;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.ReviewerChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.ApprovalChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.AssigneeChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.ReviewerChanged;
import dev.struchkov.bot.gitlab.context.domain.entity.Discussion;
import dev.struchkov.bot.gitlab.context.domain.entity.MergeRequest;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.bot.gitlab.context.domain.entity.Project;
import dev.struchkov.bot.gitlab.context.domain.event.NewMergeRequestEvent;
import dev.struchkov.bot.gitlab.context.domain.event.UpdateMergeRequestEvent;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.ApprovalChangedMrNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.ConflictMrNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.ConflictResolveMrNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.NewMrForAssignee;
@ -24,9 +26,11 @@ import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static java.lang.Boolean.TRUE;
@ -62,12 +66,9 @@ public class MergeRequestHandler {
final MergeRequest newMergeRequest = event.getNewMergeRequest();
final AssigneeChanged assigneeChanged = event.getAssigneeChanged();
final ReviewerChanged reviewerChanged = event.getReviewerChanged();
final Optional<ApprovalChanged> optApprovalChanged = event.getOptApprovalChanged();
final boolean isChangedMr = !oldMergeRequest.getUpdatedDate().equals(newMergeRequest.getUpdatedDate()) || oldMergeRequest.isConflict() != newMergeRequest.isConflict();
final boolean isChangedLinkedEntity = reviewerChanged.isChanged() || assigneeChanged.isChanged();
final boolean isMilestone = !Objects.equals(oldMergeRequest.getMilestone(), newMergeRequest.getMilestone());
if (isChangedMr || isChangedLinkedEntity || isMilestone) {
if (oldMergeRequest.isNotification()) {
final Project project = projectService.getByIdOrThrow(newMergeRequest.getProjectId());
@ -79,13 +80,43 @@ public class MergeRequestHandler {
notifyAboutUpdate(oldMergeRequest, newMergeRequest, project);
}
if (isChangedLinkedEntity) {
if (reviewerChanged.isChanged()) {
notifyReviewer(reviewerChanged, newMergeRequest, project);
}
if (assigneeChanged.isChanged()) {
notifyAssignee(assigneeChanged, oldMergeRequest, newMergeRequest, project);
}
if (optApprovalChanged.isPresent()) {
final ApprovalChanged approvalChanged = optApprovalChanged.get();
notifyApproval(newMergeRequest, project, approvalChanged);
}
}
}
private void notifyApproval(MergeRequest mergeRequest, Project project, ApprovalChanged approvalChanged) {
final Set<Person> newApproval = approvalChanged.getNewApproval().stream()
.filter(approval -> !personInformation.getId().equals(approval.getId()))
.collect(Collectors.toSet());
final Set<Person> dontApproval = approvalChanged.getDontApprove().stream()
.filter(approval -> !personInformation.getId().equals(approval.getId()))
.collect(Collectors.toSet());
if (checkNotEmpty(newApproval) || checkNotEmpty(dontApproval)) {
notifyService.send(
ApprovalChangedMrNotify.builder()
.mrId(mergeRequest.getId())
.milestone(mergeRequest.getMilestone())
.projectName(project.getName())
.title(mergeRequest.getTitle())
.url(mergeRequest.getWebUrl())
.newApproval(newApproval)
.dontApproval(dontApproval)
.build()
);
}
}
private void sendNotifyNewMrReview(MergeRequest mergeRequest, String projectName) {
@ -257,8 +288,9 @@ public class MergeRequestHandler {
private void notifyAssignee(AssigneeChanged assigneeChanged, MergeRequest oldMergeRequest, MergeRequest mergeRequest, Project project) {
switch (assigneeChanged) {
case BECOME ->
sendNotifyNewAssignee(mergeRequest, project.getName(), getAssignee(oldMergeRequest).map(Person::getName).orElse(null));
case BECOME -> sendNotifyNewAssignee(
mergeRequest, project.getName(), getAssignee(oldMergeRequest).map(Person::getName).orElse(null)
);
}
}
//TODO [05.12.2022|uPagge]: Добавить уведомление, если происходит удаление ревьювера

View File

@ -65,11 +65,11 @@ public class DiscussionParser {
private void processingNewDiscussion(MergeRequestForDiscussion mergeRequest) {
int page = 1;
final List<DiscussionJson> discussionJson = gitlabSdkManager.getDiscussionForMergeRequest(mergeRequest.getProjectId(), mergeRequest.getTwoId(), page);
final List<DiscussionJson> discussionJson = gitlabSdkManager.getDiscussionForMergeRequest(mergeRequest.getProjectId(), mergeRequest.getTwoId(), page, PAGE_COUNT);
if (checkNotEmpty(discussionJson)) {
while (discussionJson.size() == PAGE_COUNT) {
discussionJson.addAll(gitlabSdkManager.getDiscussionForMergeRequest(mergeRequest.getProjectId(), mergeRequest.getTwoId(), ++page));
discussionJson.addAll(gitlabSdkManager.getDiscussionForMergeRequest(mergeRequest.getProjectId(), mergeRequest.getTwoId(), ++page, PAGE_COUNT));
}
createNewDiscussion(discussionJson, mergeRequest);
}

View File

@ -104,7 +104,7 @@ public class MergeRequestParser {
newMergeRequests.stream()
.flatMap(mergeRequest -> Stream.of(mergeRequest.getAssignee(), mergeRequest.getAuthor())),
newMergeRequests.stream()
.flatMap(mergeRequest -> mergeRequest.getReviewers().stream())
.flatMap(mergeRequest -> Stream.concat(mergeRequest.getReviewers().stream(), mergeRequest.getApprovals().stream()))
).distinct()
.filter(Objects::nonNull)
.collect(Collectors.toMap(Person::getId, p -> p));
@ -122,6 +122,12 @@ public class MergeRequestParser {
.map(reviewer -> personMap.get(reviewer.getId()))
.collect(toList())
);
newMergeRequest.setApprovals(
newMergeRequest.getApprovals().stream()
.map(approval -> personMap.get(approval.getId()))
.collect(toList())
);
}
}

View File

@ -1,11 +1,12 @@
package dev.struchkov.bot.gitlab.core.service;
import dev.struchkov.bot.gitlab.context.domain.AssigneeChanged;
import dev.struchkov.bot.gitlab.context.domain.ExistContainer;
import dev.struchkov.bot.gitlab.context.domain.IdAndStatusPr;
import dev.struchkov.bot.gitlab.context.domain.MergeRequestState;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.ReviewerChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.ApprovalChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.AssigneeChanged;
import dev.struchkov.bot.gitlab.context.domain.changed.ReviewerChanged;
import dev.struchkov.bot.gitlab.context.domain.entity.MergeRequest;
import dev.struchkov.bot.gitlab.context.domain.entity.MergeRequestForDiscussion;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
@ -104,19 +105,18 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
final MergeRequest oldMergeRequest = repository.findById(mergeRequest.getId())
.orElseThrow(notFoundException("MergeRequest не найден"));
final MergeRequest cloneOldMergeRequest = oldMergeRequest.toBuilder().build();
mergeRequest.setNotification(oldMergeRequest.isNotification());
final Long gitlabUserId = personInformation.getId();
final AssigneeChanged assigneeChanged = AssigneeChanged.valueOf(gitlabUserId, getAssignee(oldMergeRequest), getAssignee(mergeRequest));
final ReviewerChanged reviewerChanged = ReviewerChanged.valueOf(gitlabUserId, oldMergeRequest.getReviewers(), mergeRequest.getReviewers());
final Optional<ApprovalChanged> optApprovalChanged = ApprovalChanged.approvalChanged(oldMergeRequest.getApprovals(), mergeRequest.getApprovals());
mergeRequest.setUserAssignee(assigneeChanged.getNewStatus(oldMergeRequest.isUserAssignee()));
mergeRequest.setUserReviewer(reviewerChanged.getNewStatus(oldMergeRequest.isUserReviewer()));
eventPublisher.publishEvent(
updateMergeRequest(cloneOldMergeRequest, mergeRequest, assigneeChanged, reviewerChanged)
updateMergeRequest(oldMergeRequest, mergeRequest, assigneeChanged, reviewerChanged, optApprovalChanged)
);
return repository.save(mergeRequest);

View File

@ -8,7 +8,7 @@ spring:
change-log: classpath:liquibase/changelog.xml
jpa:
show-sql: false
# open-in-view: false
open-in-view: false
hibernate:
ddl-auto: none
properties:

View File

@ -0,0 +1,83 @@
package dev.struchkov.bot.gitlab.telegram.service.notify;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.bot.gitlab.context.domain.notify.mergerequest.ApprovalChangedMrNotify;
import dev.struchkov.bot.gitlab.context.utils.Icons;
import dev.struchkov.godfather.simple.domain.BoxAnswer;
import dev.struchkov.godfather.telegram.main.context.BoxAnswerPayload;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Set;
import static dev.struchkov.bot.gitlab.telegram.utils.Const.BUTTON_ARG_CONFIRMATION;
import static dev.struchkov.bot.gitlab.telegram.utils.Const.BUTTON_ARG_DISABLE_NOTIFY_MR_ID;
import static dev.struchkov.bot.gitlab.telegram.utils.Const.BUTTON_VALUE_FALSE;
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.haiti.utils.Checker.checkNotEmpty;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
import static java.util.stream.Collectors.joining;
@Component
@RequiredArgsConstructor
public class ApprovalChangedMrNotifyGenerator implements NotifyBoxAnswerGenerator<ApprovalChangedMrNotify> {
@Override
public BoxAnswer generate(ApprovalChangedMrNotify notify) {
final StringBuilder builder = new StringBuilder(Icons.APPROVAL).append(" *Approvals changed*")
.append(Icons.HR)
.append(escapeMarkdown(notify.getTitle()));
if (checkNotNull(notify.getProjectName()) || checkNotNull(notify.getMilestone())) {
builder
.append(Icons.HR);
if (checkNotNull(notify.getProjectName())) {
builder.append(Icons.PROJECT).append(": ").append(escapeMarkdown(notify.getProjectName())).append("\n");
}
if (checkNotNull(notify.getMilestone())) {
builder.append(Icons.MILESTONE).append(": ").append(notify.getMilestone()).append("\n");
}
builder
.append("-- -- -- -- --\n");
}
final Set<Person> newApprovals = notify.getNewApproval();
if (checkNotEmpty(newApprovals)) {
builder.append(Icons.YES).append(" : ").append(newApprovals.stream().map(Person::getName).collect(joining(", ")))
.append("\n\n");
}
final Set<Person> dontApproval = notify.getDontApproval();
if (checkNotEmpty(dontApproval)) {
builder.append(Icons.NO).append(" : ").append(dontApproval.stream().map(Person::getName).collect(joining(", ")));
}
return BoxAnswer.builder()
.message(builder.toString())
.keyBoard(
inlineKeyBoard(
keyBoardLine(
simpleButton(Icons.VIEW, DELETE_MESSAGE),
urlButton(Icons.LINK, notify.getUrl()),
simpleButton(Icons.DISABLE_NOTIFY, "[" + BUTTON_ARG_DISABLE_NOTIFY_MR_ID + ":" + notify.getMrId() + ";" + BUTTON_ARG_CONFIRMATION + ":" + BUTTON_VALUE_FALSE + "]")
)
)
)
.payload(BoxAnswerPayload.ENABLE_MARKDOWN)
.build();
}
@Override
public String getNotifyType() {
return ApprovalChangedMrNotify.TYPE;
}
}