Большой рефакторинг

This commit is contained in:
Struchkov Mark 2022-12-05 22:25:59 +03:00
parent 0d57bee785
commit a6dce07b3f
12 changed files with 318 additions and 86 deletions

View File

@ -0,0 +1,40 @@
package dev.struchkov.bot.gitlab.context.domain;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Checker.checkNull;
@Getter
@RequiredArgsConstructor
public enum AssigneeChanged {
BECOME(true),
DELETED(true),
NOT_AFFECT_USER(true),
NOT_CHANGED(false);
private final boolean changed;
public static AssigneeChanged valueOf(Long gitlabUserId, Person oldAssignee, Person newAssignee) {
if (checkNull(oldAssignee) && checkNotNull(newAssignee) && gitlabUserId.equals(newAssignee.getId())) {
return AssigneeChanged.BECOME;
}
if (checkNotNull(oldAssignee) && checkNull(newAssignee) && gitlabUserId.equals(oldAssignee.getId())) {
return AssigneeChanged.DELETED;
}
if (checkNotNull(oldAssignee) && checkNotNull(newAssignee) && !oldAssignee.getId().equals(newAssignee.getId())) {
if (gitlabUserId.equals(oldAssignee.getId())) {
return AssigneeChanged.DELETED;
}
if (gitlabUserId.equals(newAssignee.getId())) {
return AssigneeChanged.BECOME;
}
return AssigneeChanged.NOT_AFFECT_USER;
}
return AssigneeChanged.NOT_CHANGED;
}
}

View File

@ -0,0 +1,38 @@
package dev.struchkov.bot.gitlab.context.domain;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Getter
@RequiredArgsConstructor
public enum ReviewerChanged {
BECOME(true),
DELETED(true),
NOT_AFFECT_USER(true),
NOT_CHANGED(false);
private final boolean changed;
public static ReviewerChanged valueOf(Long gitlabUserId, List<Person> oldReviewers, List<Person> newReviewers) {
final Map<Long, Person> oldMap = oldReviewers.stream().collect(Collectors.toMap(Person::getId, p -> p));
final Map<Long, Person> newMap = newReviewers.stream().collect(Collectors.toMap(Person::getId, p -> p));
if (!oldMap.keySet().equals(newMap.keySet())) {
if (oldMap.containsKey(gitlabUserId) && !newMap.containsKey(gitlabUserId)) {
return ReviewerChanged.DELETED;
}
if (!oldMap.containsKey(gitlabUserId) && newMap.containsKey(gitlabUserId)) {
return ReviewerChanged.BECOME;
}
return ReviewerChanged.NOT_AFFECT_USER;
}
return ReviewerChanged.NOT_CHANGED;
}
}

View File

@ -30,14 +30,14 @@ public class Discussion {
@Column(name = "id") @Column(name = "id")
private String id; private String id;
@ManyToOne @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name = "responsible_id") @JoinColumn(name = "responsible_id")
private Person responsible; private Person responsible;
@Column(name = "resolved") @Column(name = "resolved")
private Boolean resolved; private Boolean resolved;
@ManyToOne() @ManyToOne
@JoinTable( @JoinTable(
name = "discussion_merge_request", name = "discussion_merge_request",
joinColumns = @JoinColumn(name = "discussion_id"), joinColumns = @JoinColumn(name = "discussion_id"),
@ -50,7 +50,8 @@ public class Discussion {
fetch = FetchType.EAGER, fetch = FetchType.EAGER,
cascade = { cascade = {
CascadeType.PERSIST, CascadeType.PERSIST,
CascadeType.MERGE CascadeType.MERGE,
CascadeType.REFRESH
} }
) )
private List<Note> notes; private List<Note> notes;

View File

@ -73,18 +73,17 @@ public class MergeRequest {
@Column(name = "conflict") @Column(name = "conflict")
private boolean conflict; private boolean conflict;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}) @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "author_id") @JoinColumn(name = "author_id")
private Person author; private Person author;
@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}) @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "assignee_id") @JoinColumn(name = "assignee_id")
private Person assignee; private Person assignee;
@OneToMany( @OneToMany(
fetch = FetchType.LAZY, fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}, cascade = {CascadeType.PERSIST, CascadeType.MERGE}
orphanRemoval = true
) )
@JoinTable( @JoinTable(
name = "merge_request_reviewer", name = "merge_request_reviewer",

View File

@ -12,6 +12,9 @@ import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static javax.persistence.CascadeType.MERGE;
import static javax.persistence.CascadeType.PERSIST;
@Getter @Getter
@Setter @Setter
@Entity @Entity
@ -20,7 +23,8 @@ import java.time.LocalDateTime;
public class Note { public class Note {
@Id @Id
@Column @Column(name = "id")
@EqualsAndHashCode.Include
private Long id; private Long id;
@Column(name = "type") @Column(name = "type")
@ -35,7 +39,7 @@ public class Note {
@Column(name = "updated_date") @Column(name = "updated_date")
private LocalDateTime updated; private LocalDateTime updated;
@ManyToOne @ManyToOne(cascade = {PERSIST, MERGE})
@JoinColumn(name = "author_id") @JoinColumn(name = "author_id")
private Person author; private Person author;
@ -57,7 +61,7 @@ public class Note {
@Column(name = "resolved") @Column(name = "resolved")
private Boolean resolved; private Boolean resolved;
@ManyToOne @ManyToOne(cascade = {PERSIST, MERGE})
@JoinColumn(name = "resolved_id") @JoinColumn(name = "resolved_id")
private Person resolvedBy; private Person resolvedBy;

View File

@ -18,6 +18,8 @@ public interface DiscussionService {
Discussion update(@NonNull Discussion discussion); Discussion update(@NonNull Discussion discussion);
List<Discussion> updateAll(@NonNull List<Discussion> discussions);
/** /**
* Метод отправляющий коментарий в дискуссию. * Метод отправляющий коментарий в дискуссию.
* *

View File

@ -5,13 +5,13 @@ import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.entity.Discussion; 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.MergeRequest;
import dev.struchkov.bot.gitlab.context.domain.entity.Note; import dev.struchkov.bot.gitlab.context.domain.entity.Note;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.bot.gitlab.context.domain.notify.comment.CommentNotify; import dev.struchkov.bot.gitlab.context.domain.notify.comment.CommentNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskCloseNotify;
import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskNewNotify; import dev.struchkov.bot.gitlab.context.domain.notify.task.TaskNewNotify;
import dev.struchkov.bot.gitlab.context.repository.DiscussionRepository; import dev.struchkov.bot.gitlab.context.repository.DiscussionRepository;
import dev.struchkov.bot.gitlab.context.service.DiscussionService; import dev.struchkov.bot.gitlab.context.service.DiscussionService;
import dev.struchkov.bot.gitlab.context.service.NotifyService; import dev.struchkov.bot.gitlab.context.service.NotifyService;
import dev.struchkov.bot.gitlab.context.service.PersonService;
import dev.struchkov.bot.gitlab.core.config.properties.GitlabProperty; import dev.struchkov.bot.gitlab.core.config.properties.GitlabProperty;
import dev.struchkov.bot.gitlab.core.config.properties.PersonProperty; import dev.struchkov.bot.gitlab.core.config.properties.PersonProperty;
import dev.struchkov.bot.gitlab.core.utils.StringUtils; import dev.struchkov.bot.gitlab.core.utils.StringUtils;
@ -25,6 +25,7 @@ import okhttp3.RequestBody;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
@ -38,6 +39,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException; import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static java.lang.Boolean.FALSE; import static java.lang.Boolean.FALSE;
/** /**
@ -52,7 +54,6 @@ public class DiscussionServiceImpl implements DiscussionService {
protected static final Pattern PATTERN = Pattern.compile("@[\\w]+"); protected static final Pattern PATTERN = Pattern.compile("@[\\w]+");
private final PersonService personService;
private final DiscussionRepository repository; private final DiscussionRepository repository;
private final PersonInformation personInformation; private final PersonInformation personInformation;
@ -62,14 +63,18 @@ public class DiscussionServiceImpl implements DiscussionService {
private final NotifyService notifyService; private final NotifyService notifyService;
@Override @Override
@Transactional
public Discussion create(@NonNull Discussion discussion) { public Discussion create(@NonNull Discussion discussion) {
discussion.getNotes().forEach(note -> personService.create(note.getAuthor())); final List<Note> notes = discussion.getNotes();
discussion.getNotes().forEach(this::notificationPersonal);
discussion.getNotes().forEach(note -> notifyNewNote(note, discussion)); notes.forEach(this::notificationPersonal);
notes.forEach(note -> notifyNewNote(note, discussion));
final boolean resolved = discussion.getNotes().stream() final boolean resolved = discussion.getNotes().stream()
.allMatch(note -> note.isResolvable() && note.getResolved()); .allMatch(note -> note.isResolvable() && note.getResolved());
discussion.setResolved(resolved); discussion.setResolved(resolved);
return repository.save(discussion); return repository.save(discussion);
} }
@ -89,10 +94,10 @@ public class DiscussionServiceImpl implements DiscussionService {
} }
private boolean isNeedNotifyNewNote(Note note, Discussion discussion) { private boolean isNeedNotifyNewNote(Note note, Discussion discussion) {
final Long personId = personInformation.getId(); final Long gitlabUserId = personInformation.getId();
return note.isResolvable() // Тип комментария требует решения (Задачи) return note.isResolvable() // Тип комментария требует решения (Задачи)
&& personId.equals(discussion.getResponsible().getId()) // Создатель дискуссии пользователь приложения && gitlabUserId.equals(discussion.getResponsible().getId()) // Создатель дискуссии пользователь приложения
&& !personId.equals(note.getAuthor().getId()) // Создатель комментария не пользователь системы && !gitlabUserId.equals(note.getAuthor().getId()) // Создатель комментария не пользователь системы
&& FALSE.equals(note.getResolved()); // Комментарий не отмечен как решенный && FALSE.equals(note.getResolved()); // Комментарий не отмечен как решенный
} }
@ -100,40 +105,68 @@ public class DiscussionServiceImpl implements DiscussionService {
public Discussion update(@NonNull Discussion discussion) { public Discussion update(@NonNull Discussion discussion) {
final Discussion oldDiscussion = repository.findById(discussion.getId()) final Discussion oldDiscussion = repository.findById(discussion.getId())
.orElseThrow(notFoundException("Дискуссия не найдена")); .orElseThrow(notFoundException("Дискуссия не найдена"));
final Map<Long, Note> idAndNoteMap = oldDiscussion
.getNotes().stream()
.collect(Collectors.toMap(Note::getId, note -> note));
// Пользователь участвовал в обсуждении
final boolean userParticipatedInDiscussion = discussion.getNotes().stream()
.anyMatch(note -> personInformation.getId().equals(note.getAuthor().getId()));
discussion.setMergeRequest(oldDiscussion.getMergeRequest());
discussion.setResponsible(oldDiscussion.getResponsible()); discussion.setResponsible(oldDiscussion.getResponsible());
discussion.getNotes().forEach(note -> updateNote(note, idAndNoteMap, userParticipatedInDiscussion)); discussion.setMergeRequest(oldDiscussion.getMergeRequest());
final Person responsiblePerson = discussion.getResponsible();
if (checkNotNull(responsiblePerson)) {
for (Note note : discussion.getNotes()) {
if (responsiblePerson.getId().equals(note.getAuthor().getId())) {
note.setAuthor(responsiblePerson);
}
final Person resolvedBy = note.getResolvedBy();
if (checkNotNull(resolvedBy)) {
if (responsiblePerson.getId().equals(resolvedBy.getId())) {
note.setResolvedBy(responsiblePerson);
}
}
}
}
notifyUpdateNote(oldDiscussion, discussion);
final boolean resolved = discussion.getNotes().stream() final boolean resolved = discussion.getNotes().stream()
.allMatch(note -> note.isResolvable() && note.getResolved()); .allMatch(note -> note.isResolvable() && note.getResolved());
discussion.setResolved(resolved); discussion.setResolved(resolved);
return repository.save(discussion); return repository.save(discussion);
} }
private void updateNote(Note note, Map<Long, Note> noteMap, boolean inDiscussion) { @Override
if (noteMap.containsKey(note.getId())) { public List<Discussion> updateAll(@NonNull List<Discussion> discussions) {
final Note oldNote = noteMap.get(note.getId()); return discussions.stream()
.map(this::update)
.collect(Collectors.toList());
}
if (note.isResolvable()) { private void notifyUpdateNote(Discussion oldDiscussion, Discussion discussion) {
updateTask(note, oldNote); final Map<Long, Note> noteMap = oldDiscussion
} .getNotes().stream()
.collect(Collectors.toMap(Note::getId, n -> n));
// Пользователь участвовал в обсуждении
final boolean userParticipatedInDiscussion = oldDiscussion.getNotes().stream()
.anyMatch(note -> personInformation.getId().equals(note.getAuthor().getId()));
for (Note newNote : discussion.getNotes()) {
final Long newNoteId = newNote.getId();
if (noteMap.containsKey(newNoteId)) {
final Note oldNote = noteMap.get(newNoteId);
if (newNote.isResolvable()) {
updateTask(newNote, oldNote);
}
} else {
if (inDiscussion) {
notifyNewAnswer(note);
} else { } else {
notificationPersonal(note); if (userParticipatedInDiscussion) {
notifyNewAnswer(newNote);
} else {
notificationPersonal(newNote);
}
} }
} }
} }
private void notifyNewAnswer(Note note) { private void notifyNewAnswer(Note note) {

View File

@ -1,9 +1,11 @@
package dev.struchkov.bot.gitlab.core.service.impl; package dev.struchkov.bot.gitlab.core.service.impl;
import dev.struchkov.bot.gitlab.context.domain.AssigneeChanged;
import dev.struchkov.bot.gitlab.context.domain.ExistContainer; import dev.struchkov.bot.gitlab.context.domain.ExistContainer;
import dev.struchkov.bot.gitlab.context.domain.IdAndStatusPr; import dev.struchkov.bot.gitlab.context.domain.IdAndStatusPr;
import dev.struchkov.bot.gitlab.context.domain.MergeRequestState; import dev.struchkov.bot.gitlab.context.domain.MergeRequestState;
import dev.struchkov.bot.gitlab.context.domain.PersonInformation; import dev.struchkov.bot.gitlab.context.domain.PersonInformation;
import dev.struchkov.bot.gitlab.context.domain.ReviewerChanged;
import dev.struchkov.bot.gitlab.context.domain.entity.Discussion; 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.MergeRequest;
import dev.struchkov.bot.gitlab.context.domain.entity.Person; import dev.struchkov.bot.gitlab.context.domain.entity.Person;
@ -117,24 +119,29 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
if (isUserInReviewers) { if (isUserInReviewers) {
final String projectName = projectService.getByIdOrThrow(savedMergeRequest.getProjectId()).getName(); final String projectName = projectService.getByIdOrThrow(savedMergeRequest.getProjectId()).getName();
if (!savedMergeRequest.isConflict()) { if (!savedMergeRequest.isConflict()) {
notifyService.send( sendNotifyAboutNewMr(savedMergeRequest, projectName);
NewPrNotify.builder()
.projectName(projectName)
.labels(savedMergeRequest.getLabels())
.author(savedMergeRequest.getAuthor().getName())
.description(savedMergeRequest.getDescription())
.title(savedMergeRequest.getTitle())
.url(savedMergeRequest.getWebUrl())
.targetBranch(savedMergeRequest.getTargetBranch())
.sourceBranch(savedMergeRequest.getSourceBranch())
.build()
);
} }
} }
} }
} }
private void sendNotifyAboutNewMr(MergeRequest savedMergeRequest, String projectName) {
notifyService.send(
NewPrNotify.builder()
.projectName(projectName)
.labels(savedMergeRequest.getLabels())
.author(savedMergeRequest.getAuthor().getName())
.description(savedMergeRequest.getDescription())
.title(savedMergeRequest.getTitle())
.url(savedMergeRequest.getWebUrl())
.targetBranch(savedMergeRequest.getTargetBranch())
.sourceBranch(savedMergeRequest.getSourceBranch())
.build()
);
}
@Override @Override
@Transactional
public MergeRequest update(@NonNull MergeRequest mergeRequest) { public MergeRequest update(@NonNull MergeRequest mergeRequest) {
final MergeRequest oldMergeRequest = repository.findById(mergeRequest.getId()) final MergeRequest oldMergeRequest = repository.findById(mergeRequest.getId())
.orElseThrow(notFoundException("MergeRequest не найден")); .orElseThrow(notFoundException("MergeRequest не найден"));
@ -144,24 +151,53 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
mergeRequest.setNotification(oldMergeRequest.getNotification()); mergeRequest.setNotification(oldMergeRequest.getNotification());
} }
if ( final Long gitlabUserId = personInformation.getId();
final AssigneeChanged assigneeChanged = AssigneeChanged.valueOf(gitlabUserId, oldMergeRequest.getAssignee(), mergeRequest.getAssignee());
final ReviewerChanged reviewerChanged = ReviewerChanged.valueOf(gitlabUserId, oldMergeRequest.getReviewers(), mergeRequest.getReviewers());
final boolean isChangedMr =
!oldMergeRequest.getUpdatedDate().equals(mergeRequest.getUpdatedDate()) !oldMergeRequest.getUpdatedDate().equals(mergeRequest.getUpdatedDate())
|| oldMergeRequest.isConflict() != mergeRequest.isConflict() || oldMergeRequest.isConflict() != mergeRequest.isConflict();
) { final boolean isChangedLinkedEntity = reviewerChanged.isChanged() || assigneeChanged.isChanged();
if (isChangedMr || isChangedLinkedEntity) {
final Project project = projectService.getByIdOrThrow(mergeRequest.getProjectId()); final Project project = projectService.getByIdOrThrow(mergeRequest.getProjectId());
if (TRUE.equals(oldMergeRequest.getNotification())) { if (TRUE.equals(notification) && isChangedMr) {
notifyAboutStatus(oldMergeRequest, mergeRequest, project); notifyAboutStatus(oldMergeRequest, mergeRequest, project);
notifyAboutConflict(oldMergeRequest, mergeRequest, project); notifyAboutConflict(oldMergeRequest, mergeRequest, project);
notifyUpdate(oldMergeRequest, mergeRequest, project); notifyAboutUpdate(oldMergeRequest, mergeRequest, project);
}
if (TRUE.equals(notification) && isChangedLinkedEntity) {
notifyReviewer(reviewerChanged, mergeRequest, project);
notifyAssignee(assigneeChanged, mergeRequest, project);
} }
return repository.save(mergeRequest); return repository.save(mergeRequest);
} }
return oldMergeRequest; return oldMergeRequest;
} }
//TODO [05.12.2022|uPagge]: Добавить уведомление, если происходит удаление
private void notifyAssignee(AssigneeChanged assigneeChanged, MergeRequest mergeRequest, Project project) {
switch (assigneeChanged) {
case BECOME -> sendNotifyAboutNewMr(mergeRequest, project.getName());
}
}
//TODO [05.12.2022|uPagge]: Добавить уведомление, если происходит удаление ревьювера
//TODO [05.12.2022|uPagge]: Заменить тип уведомления на самостоятельный
private void notifyReviewer(ReviewerChanged reviewerChanged, MergeRequest mergeRequest, Project project) {
switch (reviewerChanged) {
case BECOME -> sendNotifyAboutNewMr(mergeRequest, project.getName());
}
}
@Override @Override
@Transactional
public List<MergeRequest> updateAll(@NonNull List<MergeRequest> mergeRequests) { public List<MergeRequest> updateAll(@NonNull List<MergeRequest> mergeRequests) {
return mergeRequests.stream() return mergeRequests.stream()
.map(this::update) .map(this::update)
@ -214,11 +250,13 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
return repository.findAllByReviewerId(personId); return repository.findAllByReviewerId(personId);
} }
private void notifyUpdate(MergeRequest oldMergeRequest, MergeRequest mergeRequest, Project project) { private void notifyAboutUpdate(MergeRequest oldMergeRequest, MergeRequest mergeRequest, Project project) {
final Long gitlabUserId = personInformation.getId();
if ( if (
!personInformation.getId().equals(mergeRequest.getAuthor().getId()) !gitlabUserId.equals(mergeRequest.getAuthor().getId()) // Автор MR не пользователь приложения
&& !oldMergeRequest.getDateLastCommit().equals(mergeRequest.getDateLastCommit()) && !oldMergeRequest.getDateLastCommit().equals(mergeRequest.getDateLastCommit()) // Изменилась дата последнего коммита
&& !mergeRequest.isConflict() && !mergeRequest.isConflict() // MR не находится в состоянии конфликта
) { ) {
final List<Discussion> discussions = discussionService.getAllByMergeRequestId(oldMergeRequest.getId()) final List<Discussion> discussions = discussionService.getAllByMergeRequestId(oldMergeRequest.getId())
.stream() .stream()
@ -229,10 +267,10 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
.filter(Discussion::getResolved) .filter(Discussion::getResolved)
.count(); .count();
final long allYouTasks = discussions.stream() final long allYouTasks = discussions.stream()
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId())) .filter(discussion -> gitlabUserId.equals(discussion.getFirstNote().getAuthor().getId()))
.count(); .count();
final long resolvedYouTask = discussions.stream() final long resolvedYouTask = discussions.stream()
.filter(discussion -> personInformation.getId().equals(discussion.getFirstNote().getAuthor().getId()) && discussion.getResolved()) .filter(discussion -> gitlabUserId.equals(discussion.getFirstNote().getAuthor().getId()) && discussion.getResolved())
.count(); .count();
notifyService.send( notifyService.send(
UpdatePrNotify.builder() UpdatePrNotify.builder()
@ -250,10 +288,11 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
} }
protected void notifyAboutConflict(MergeRequest oldMergeRequest, MergeRequest mergeRequest, Project project) { protected void notifyAboutConflict(MergeRequest oldMergeRequest, MergeRequest mergeRequest, Project project) {
final Long gitlabUserId = personInformation.getId();
if ( if (
!oldMergeRequest.isConflict() !oldMergeRequest.isConflict() // У старого MR не было конфликта
&& mergeRequest.isConflict() && mergeRequest.isConflict() // А у нового есть
&& personInformation.getId().equals(oldMergeRequest.getAuthor().getId()) && gitlabUserId.equals(oldMergeRequest.getAuthor().getId()) // и MR создан пользователем бота
) { ) {
notifyService.send( notifyService.send(
ConflictPrNotify.builder() ConflictPrNotify.builder()
@ -269,9 +308,10 @@ public class MergeRequestsServiceImpl implements MergeRequestsService {
protected void notifyAboutStatus(MergeRequest oldMergeRequest, MergeRequest newMergeRequest, Project project) { protected void notifyAboutStatus(MergeRequest oldMergeRequest, MergeRequest newMergeRequest, Project project) {
final MergeRequestState oldStatus = oldMergeRequest.getState(); final MergeRequestState oldStatus = oldMergeRequest.getState();
final MergeRequestState newStatus = newMergeRequest.getState(); final MergeRequestState newStatus = newMergeRequest.getState();
final Long gitlabUserId = personInformation.getId();
if ( if (
!oldStatus.equals(newStatus) !oldStatus.equals(newStatus) // статус изменился
&& oldMergeRequest.getAuthor().getId().equals(personInformation.getId()) && gitlabUserId.equals(oldMergeRequest.getAuthor().getId()) // создатель MR является пользователем бота
) { ) {
notifyService.send( notifyService.send(
StatusPrNotify.builder() StatusPrNotify.builder()

View File

@ -4,6 +4,7 @@ import dev.struchkov.bot.gitlab.context.domain.ExistContainer;
import dev.struchkov.bot.gitlab.context.domain.entity.Discussion; 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.MergeRequest;
import dev.struchkov.bot.gitlab.context.domain.entity.Note; import dev.struchkov.bot.gitlab.context.domain.entity.Note;
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
import dev.struchkov.bot.gitlab.context.service.DiscussionService; import dev.struchkov.bot.gitlab.context.service.DiscussionService;
import dev.struchkov.bot.gitlab.context.service.MergeRequestsService; import dev.struchkov.bot.gitlab.context.service.MergeRequestsService;
import dev.struchkov.bot.gitlab.core.config.properties.GitlabProperty; import dev.struchkov.bot.gitlab.core.config.properties.GitlabProperty;
@ -17,13 +18,20 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static dev.struchkov.bot.gitlab.core.utils.StringUtils.H_PRIVATE_TOKEN; import static dev.struchkov.bot.gitlab.core.utils.StringUtils.H_PRIVATE_TOKEN;
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
import static dev.struchkov.haiti.utils.Checker.checkNotNull;
import static dev.struchkov.haiti.utils.Checker.checkNull;
import static dev.struchkov.haiti.utils.network.HttpParse.ACCEPT; import static dev.struchkov.haiti.utils.network.HttpParse.ACCEPT;
/** /**
@ -52,8 +60,12 @@ public class DiscussionParser {
Page<MergeRequest> mergeRequestSheet = mergeRequestsService.getAll(PageRequest.of(page, COUNT)); Page<MergeRequest> mergeRequestSheet = mergeRequestsService.getAll(PageRequest.of(page, COUNT));
while (mergeRequestSheet.hasContent()) { while (mergeRequestSheet.hasContent()) {
mergeRequestSheet.getContent() final List<MergeRequest> mergeRequests = mergeRequestSheet.getContent();
.forEach(this::processingMergeRequest);
for (MergeRequest mergeRequest : mergeRequests) {
processingMergeRequest(mergeRequest);
}
mergeRequestSheet = mergeRequestsService.getAll(PageRequest.of(++page, COUNT)); mergeRequestSheet = mergeRequestsService.getAll(PageRequest.of(++page, COUNT));
} }
} }
@ -86,9 +98,52 @@ public class DiscussionParser {
discussion.getNotes().forEach(createNoteLink(mergeRequest)); discussion.getNotes().forEach(createNoteLink(mergeRequest));
return discussion; return discussion;
}) })
.filter(discussion -> discussion.getNotes() != null && !discussion.getNotes().isEmpty()) // Фильтрация специально стоит после map(). Таким образом отбрасываются системные уведомления
.filter(discussion -> checkNotEmpty(discussion.getNotes()))
.toList(); .toList();
discussionService.createAll(newDiscussions);
if (checkNotEmpty(newDiscussions)) {
personMapping(newDiscussions);
discussionService.createAll(newDiscussions);
}
}
}
private void personMapping(List<Discussion> newDiscussions) {
final Stream<Person> firstStream = Stream.concat(
newDiscussions.stream()
.flatMap(discussion -> discussion.getNotes().stream())
.map(Note::getResolvedBy)
.filter(Objects::nonNull),
newDiscussions.stream()
.flatMap(discussion -> discussion.getNotes().stream())
.map(Note::getAuthor)
.filter(Objects::nonNull)
);
final Map<Long, Person> personMap = Stream.concat(
firstStream,
newDiscussions.stream()
.map(Discussion::getResponsible)
.filter(Objects::nonNull)
).distinct()
.collect(Collectors.toMap(Person::getId, p -> p));
for (Discussion newDiscussion : newDiscussions) {
final Person responsible = newDiscussion.getResponsible();
if (checkNotNull(responsible)) {
newDiscussion.setResponsible(personMap.get(responsible.getId()));
}
for (Note note : newDiscussion.getNotes()) {
note.setAuthor(personMap.get(note.getAuthor().getId()));
final Person resolvedBy = note.getResolvedBy();
if (checkNotNull(resolvedBy)) {
note.setResolvedBy(personMap.get(resolvedBy.getId()));
}
}
} }
} }
@ -97,33 +152,47 @@ public class DiscussionParser {
*/ */
public void scanOldDiscussions() { public void scanOldDiscussions() {
int page = 0; int page = 0;
Page<Discussion> discussionSheet = discussionService.getAll(PageRequest.of(page, COUNT)); Page<Discussion> discussionPage = discussionService.getAll(PageRequest.of(page, COUNT));
while (discussionSheet.hasContent()) { while (discussionPage.hasContent()) {
final List<Discussion> discussions = discussionSheet.getContent(); final List<Discussion> discussions = discussionPage.getContent();
// Удаляем обсуждения, которые потеряли свои MR
//TODO [05.12.2022|uPagge]: Проверить целесообразность этого действия
discussions.stream()
.filter(discussion -> checkNull(discussion.getMergeRequest()))
.map(Discussion::getId)
.forEach(discussionService::deleteById);
final List<Discussion> newDiscussions = new ArrayList<>();
for (Discussion discussion : discussions) { for (Discussion discussion : discussions) {
if (discussion.getMergeRequest() != null) { if (checkNotNull(discussion.getMergeRequest())) {
final Optional<Discussion> optNewDiscussion = HttpParse.request(createLinkOldDiscussion(discussion)) getOldDiscussionJson(discussion)
.header(ACCEPT)
.header(H_PRIVATE_TOKEN, personProperty.getToken())
.execute(DiscussionJson.class)
.map(json -> { .map(json -> {
final Discussion newDiscussion = conversionService.convert(json, Discussion.class); final Discussion newDiscussion = conversionService.convert(json, Discussion.class);
newDiscussion.getNotes().forEach(createNoteLink(discussion.getMergeRequest())); newDiscussion.getNotes().forEach(createNoteLink(discussion.getMergeRequest()));
return newDiscussion; return newDiscussion;
}); }).ifPresent(newDiscussions::add);
optNewDiscussion.ifPresent(discussionService::update);
} else {
discussionService.deleteById(discussion.getId());
} }
} }
discussionSheet = discussionService.getAll(PageRequest.of(++page, COUNT)); if (checkNotEmpty(newDiscussions)) {
personMapping(newDiscussions);
discussionService.updateAll(newDiscussions);
}
discussionPage = discussionService.getAll(PageRequest.of(++page, COUNT));
} }
} }
private Optional<DiscussionJson> getOldDiscussionJson(Discussion discussion) {
return HttpParse.request(createLinkOldDiscussion(discussion))
.header(ACCEPT)
.header(H_PRIVATE_TOKEN, personProperty.getToken())
.execute(DiscussionJson.class);
}
private String createLinkOldDiscussion(Discussion discussion) { private String createLinkOldDiscussion(Discussion discussion) {
return MessageFormat.format( return MessageFormat.format(
gitlabProperty.getUrlOneDiscussion(), gitlabProperty.getUrlOneDiscussion(),
@ -142,7 +211,11 @@ public class DiscussionParser {
private Consumer<Note> createNoteLink(MergeRequest mergeRequest) { private Consumer<Note> createNoteLink(MergeRequest mergeRequest) {
return note -> { return note -> {
final String url = MessageFormat.format(gitlabProperty.getUrlNote(), mergeRequest.getWebUrl(), note.getId()); final String url = MessageFormat.format(
gitlabProperty.getUrlNote(),
mergeRequest.getWebUrl(),
note.getId()
);
note.setWebUrl(url); note.setWebUrl(url);
}; };
} }

View File

@ -21,8 +21,8 @@
<artifactId>bot-context</artifactId> <artifactId>bot-context</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.liquibase</groupId> <groupId>org.liquibase</groupId>

View File

@ -5,6 +5,7 @@ import dev.struchkov.bot.gitlab.context.repository.DiscussionRepository;
import dev.struchkov.bot.gitlab.data.jpa.DiscussionJpaRepository; import dev.struchkov.bot.gitlab.data.jpa.DiscussionJpaRepository;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -16,6 +17,7 @@ import java.util.Set;
/** /**
* @author upagge 11.02.2021 * @author upagge 11.02.2021
*/ */
@Slf4j
@Repository @Repository
@RequiredArgsConstructor @RequiredArgsConstructor
public class DiscussionRepositoryImpl implements DiscussionRepository { public class DiscussionRepositoryImpl implements DiscussionRepository {

View File

@ -7,7 +7,7 @@ spring:
liquibase: liquibase:
change-log: classpath:liquibase/changelog.xml change-log: classpath:liquibase/changelog.xml
jpa: jpa:
show-sql: false show-sql: true
hibernate: hibernate:
ddl-auto: none ddl-auto: none
database-platform: org.hibernate.dialect.PostgreSQLDialect database-platform: org.hibernate.dialect.PostgreSQLDialect