Feat: завершен основной функционал issue, выгрузка, удаление, уведомления
This commit is contained in:
parent
30ff49eb75
commit
7e1c6c1f44
@ -0,0 +1,50 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [25.01.2023]
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public enum AssigneesChanged {
|
||||||
|
|
||||||
|
BECOME(true),
|
||||||
|
DELETED(true),
|
||||||
|
NOT_AFFECT_USER(true),
|
||||||
|
NOT_CHANGED(false);
|
||||||
|
|
||||||
|
private final boolean changed;
|
||||||
|
|
||||||
|
public static AssigneesChanged valueOf(Long gitlabUserId, List<Person> oldAssignees, List<Person> newAssignees) {
|
||||||
|
final Map<Long, Person> oldMap = oldAssignees.stream().collect(Collectors.toMap(Person::getId, p -> p));
|
||||||
|
final Map<Long, Person> newMap = newAssignees.stream().collect(Collectors.toMap(Person::getId, p -> p));
|
||||||
|
|
||||||
|
if (!oldMap.keySet().equals(newMap.keySet())) {
|
||||||
|
|
||||||
|
if (oldMap.containsKey(gitlabUserId) && !newMap.containsKey(gitlabUserId)) {
|
||||||
|
return AssigneesChanged.DELETED;
|
||||||
|
}
|
||||||
|
if (!oldMap.containsKey(gitlabUserId) && newMap.containsKey(gitlabUserId)) {
|
||||||
|
return AssigneesChanged.BECOME;
|
||||||
|
}
|
||||||
|
return AssigneesChanged.NOT_AFFECT_USER;
|
||||||
|
}
|
||||||
|
return AssigneesChanged.NOT_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getNewStatus(boolean oldStatus) {
|
||||||
|
return switch (this) {
|
||||||
|
case BECOME -> true;
|
||||||
|
case DELETED -> false;
|
||||||
|
case NOT_AFFECT_USER, NOT_CHANGED -> oldStatus;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmotry Sheyko [25.01.2023]
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class IdAndStatusIssue {
|
||||||
|
|
||||||
|
private long id;
|
||||||
|
private long twoId;
|
||||||
|
private long projectId;
|
||||||
|
private IssueState status;
|
||||||
|
|
||||||
|
}
|
@ -22,6 +22,7 @@ import javax.persistence.JoinTable;
|
|||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -76,11 +77,10 @@ public class Issue {
|
|||||||
private Person closedBy;
|
private Person closedBy;
|
||||||
|
|
||||||
@ElementCollection
|
@ElementCollection
|
||||||
@CollectionTable(name = "issue_labels", joinColumns = @JoinColumn(name = "label_id"))
|
@CollectionTable(name = "issue_label", joinColumns = @JoinColumn(name = "issue_id"))
|
||||||
@Column(name = "labels")
|
@Column(name = "label")
|
||||||
private Set<String> labels = new HashSet<>();
|
private Set<String> labels = new HashSet<>();
|
||||||
|
|
||||||
@Column(name = "assignees")
|
|
||||||
@OneToMany(
|
@OneToMany(
|
||||||
fetch = FetchType.LAZY,
|
fetch = FetchType.LAZY,
|
||||||
cascade = {CascadeType.PERSIST, CascadeType.MERGE}
|
cascade = {CascadeType.PERSIST, CascadeType.MERGE}
|
||||||
@ -113,7 +113,7 @@ public class Issue {
|
|||||||
private Integer downVotes;
|
private Integer downVotes;
|
||||||
|
|
||||||
@Column(name = "due_date")
|
@Column(name = "due_date")
|
||||||
private LocalDateTime dueDate;
|
private LocalDate dueDate;
|
||||||
|
|
||||||
@Column(name = "confidential")
|
@Column(name = "confidential")
|
||||||
private Boolean confidential;
|
private Boolean confidential;
|
||||||
@ -136,4 +136,10 @@ public class Issue {
|
|||||||
@Column(name = "has_tasks")
|
@Column(name = "has_tasks")
|
||||||
private Boolean hasTasks;
|
private Boolean hasTasks;
|
||||||
|
|
||||||
|
@Column(name = "notification")
|
||||||
|
private boolean notification;
|
||||||
|
|
||||||
|
@Column(name = "is_assignee")
|
||||||
|
private boolean userAssignee;
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 25.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class DeleteFromAssigneesNotify extends IssueNotify {
|
||||||
|
|
||||||
|
public static final String TYPE = "DeleteFromAssigneesOfIssueNotify";
|
||||||
|
|
||||||
|
private final String updateDate;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public DeleteFromAssigneesNotify(
|
||||||
|
String projectName,
|
||||||
|
String title,
|
||||||
|
String url,
|
||||||
|
String issueType,
|
||||||
|
String updateDate
|
||||||
|
) {
|
||||||
|
super(projectName, title, url, issueType);
|
||||||
|
this.updateDate = updateDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 25.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class DescriptionIssueNotify extends IssueNotify {
|
||||||
|
|
||||||
|
public static final String TYPE = "DescriptionIssueNotify";
|
||||||
|
|
||||||
|
private final String newDescription;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public DescriptionIssueNotify(
|
||||||
|
String projectName,
|
||||||
|
String title,
|
||||||
|
String url,
|
||||||
|
String issueType,
|
||||||
|
String newDescription
|
||||||
|
) {
|
||||||
|
super(projectName, title, url, issueType);
|
||||||
|
this.newDescription = newDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 25.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class DueDateIssueNotify extends IssueNotify {
|
||||||
|
|
||||||
|
public static final String TYPE = "DueDateIssueNotify";
|
||||||
|
|
||||||
|
private final String oldDueDate;
|
||||||
|
private final String newDueDate;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public DueDateIssueNotify(
|
||||||
|
String projectName,
|
||||||
|
String title,
|
||||||
|
String url,
|
||||||
|
String issueType,
|
||||||
|
String oldDueDate,
|
||||||
|
String newDueDate
|
||||||
|
) {
|
||||||
|
super(projectName, title, url, issueType);
|
||||||
|
this.oldDueDate = oldDueDate;
|
||||||
|
this.newDueDate = newDueDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.Notify;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 23.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public abstract class IssueNotify implements Notify {
|
||||||
|
|
||||||
|
protected final String projectName;
|
||||||
|
protected final String title;
|
||||||
|
protected final String url;
|
||||||
|
protected final String issueType;
|
||||||
|
|
||||||
|
public IssueNotify(
|
||||||
|
String projectName,
|
||||||
|
String title,
|
||||||
|
String url,
|
||||||
|
String issueType
|
||||||
|
) {
|
||||||
|
this.projectName = projectName;
|
||||||
|
this.title = title;
|
||||||
|
this.url = url;
|
||||||
|
this.issueType = issueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 23.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class NewIssueNotify extends IssueNotify {
|
||||||
|
|
||||||
|
public static final String TYPE = "NewIssueNotify";
|
||||||
|
|
||||||
|
private final String author;
|
||||||
|
private final String description;
|
||||||
|
private final String dueDate;
|
||||||
|
private final Set<String> labels;
|
||||||
|
private final String confidential;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public NewIssueNotify(
|
||||||
|
String projectName,
|
||||||
|
String title,
|
||||||
|
String url,
|
||||||
|
String issueType,
|
||||||
|
String author,
|
||||||
|
String description,
|
||||||
|
String dueDate,
|
||||||
|
Set<String> labels,
|
||||||
|
String confidential
|
||||||
|
) {
|
||||||
|
super(projectName, title, url, issueType);
|
||||||
|
this.author = author;
|
||||||
|
this.description = description;
|
||||||
|
this.dueDate = dueDate;
|
||||||
|
this.labels = labels;
|
||||||
|
this.confidential = confidential;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IssueState;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 23.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class StatusIssueNotify extends IssueNotify{
|
||||||
|
|
||||||
|
public static final String TYPE = "StatusIssueNotify";
|
||||||
|
|
||||||
|
private final IssueState oldStatus;
|
||||||
|
private final IssueState newStatus;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
private StatusIssueNotify(
|
||||||
|
String name,
|
||||||
|
String url,
|
||||||
|
String projectName,
|
||||||
|
String issueType,
|
||||||
|
IssueState oldStatus,
|
||||||
|
IssueState newStatus
|
||||||
|
) {
|
||||||
|
super(projectName, name, url, issueType);
|
||||||
|
this.oldStatus = oldStatus;
|
||||||
|
this.newStatus = newStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 25.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class TitleIssueNotify extends IssueNotify {
|
||||||
|
|
||||||
|
public static final String TYPE = "TitleIssueNotify";
|
||||||
|
|
||||||
|
private final String newTitle;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public TitleIssueNotify(
|
||||||
|
String projectName,
|
||||||
|
String title,
|
||||||
|
String url,
|
||||||
|
String issueType,
|
||||||
|
String newTitle
|
||||||
|
) {
|
||||||
|
super(projectName, title, url, issueType);
|
||||||
|
this.newTitle = newTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.domain.notify.issue;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IssueType;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 25.01.2021
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
public class TypeIssueNotify extends IssueNotify {
|
||||||
|
|
||||||
|
public static final String TYPE = "TypeIssueNotify";
|
||||||
|
|
||||||
|
private final IssueType oldType;
|
||||||
|
private final IssueType newType;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
public TypeIssueNotify(
|
||||||
|
String projectName,
|
||||||
|
String title,
|
||||||
|
String url,
|
||||||
|
String issueType,
|
||||||
|
IssueType oldType,
|
||||||
|
IssueType newType
|
||||||
|
) {
|
||||||
|
super(projectName, title, url, issueType);
|
||||||
|
this.oldType = oldType;
|
||||||
|
this.newType = newType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.repository;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IdAndStatusIssue;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IssueState;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Issue;
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [24.01.2023]
|
||||||
|
*/
|
||||||
|
public interface IssueRepository {
|
||||||
|
|
||||||
|
Set<IdAndStatusIssue> findAllIdByStateIn(@NonNull Set<IssueState> states);
|
||||||
|
|
||||||
|
Issue save(Issue issue);
|
||||||
|
|
||||||
|
Optional<Issue> findById(Long issueId);
|
||||||
|
|
||||||
|
List<Issue> findAllById(Set<Long> mergeRequestIds);
|
||||||
|
|
||||||
|
void deleteByStates(Set<IssueState> states);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.context.service;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.*;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Issue;
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [24.01.2023]
|
||||||
|
*/
|
||||||
|
public interface IssueService {
|
||||||
|
|
||||||
|
Issue create(@NonNull Issue issue);
|
||||||
|
|
||||||
|
Issue update(@NonNull Issue issue);
|
||||||
|
|
||||||
|
List<Issue> updateAll(@NonNull List<Issue> issues);
|
||||||
|
|
||||||
|
ExistContainer<Issue, Long> existsById(@NonNull Set<Long> issueIds);
|
||||||
|
|
||||||
|
List<Issue> createAll(List<Issue> issues);
|
||||||
|
|
||||||
|
Set<IdAndStatusIssue> getAllId(Set<IssueState> statuses);
|
||||||
|
|
||||||
|
void cleanOld();
|
||||||
|
|
||||||
|
}
|
@ -19,6 +19,7 @@ public class Icons {
|
|||||||
public static final String PEN = "✏️";
|
public static final String PEN = "✏️";
|
||||||
public static final String BUILD = "\uD83D\uDEE0";
|
public static final String BUILD = "\uD83D\uDEE0";
|
||||||
public static final String LINK = "\uD83D\uDD17";
|
public static final String LINK = "\uD83D\uDD17";
|
||||||
|
public static final String BELL ="\uD83D\uDD14";
|
||||||
|
|
||||||
private Icons() {
|
private Icons() {
|
||||||
utilityClass();
|
utilityClass();
|
||||||
|
@ -68,4 +68,11 @@ public class GitlabProperty {
|
|||||||
private String discussionUrl;
|
private String discussionUrl;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Адрес, по которому можно получить ISSUE
|
||||||
|
*/
|
||||||
|
private String issueUrl;
|
||||||
|
|
||||||
|
private String openIssueUrl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.core.service.convert;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IssueState;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IssueType;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Issue;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
|
||||||
|
import dev.struchkov.bot.gitlab.sdk.domain.*;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [22.01.2023]
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IssueJsonConverter implements Converter<IssueJson, Issue> {
|
||||||
|
|
||||||
|
private final PersonJsonConverter convertPerson;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Issue convert(IssueJson source) {
|
||||||
|
final Issue issue = new Issue();
|
||||||
|
issue.setId(source.getId());
|
||||||
|
issue.setTwoId(source.getTwoId());
|
||||||
|
issue.setProjectId(source.getProjectId());
|
||||||
|
issue.setTitle(source.getTitle());
|
||||||
|
issue.setDescription(source.getDescription());
|
||||||
|
issue.setState(convertState(source.getState()));
|
||||||
|
issue.setCreatedDate(source.getCreatedDate());
|
||||||
|
issue.setUpdatedDate(source.getUpdatedDate());
|
||||||
|
issue.setCloseDate(source.getClosedDate());
|
||||||
|
issue.setType(convertType(source.getType()));
|
||||||
|
issue.setUserNotesCount(source.getUserNotesCount());
|
||||||
|
issue.setMergeRequestsCount(source.getMergeRequestsCount());
|
||||||
|
issue.setUpVotes(source.getUpVotes());
|
||||||
|
issue.setDownVotes(source.getDownVotes());
|
||||||
|
issue.setDueDate(source.getDueDate());
|
||||||
|
issue.setConfidential(source.getConfidential());
|
||||||
|
issue.setDiscussionLocked(source.getDiscussionLocked());
|
||||||
|
issue.setTaskCount(source.getTaskCompletionStatus().getCount());
|
||||||
|
issue.setTaskCompletedCount(source.getTaskCompletionStatus().getCompletedCount());
|
||||||
|
issue.setWebUrl(source.getWebUrl());
|
||||||
|
issue.setBlockingIssuesCount(source.getBlockingIssuesCount());
|
||||||
|
issue.setHasTasks(source.getHasTasks());
|
||||||
|
|
||||||
|
convertAssignees(issue, source.getAssignees());
|
||||||
|
convertLabels(issue, source.getLabels());
|
||||||
|
|
||||||
|
if (checkNotNull(source.getClosedBy())) {
|
||||||
|
issue.setClosedBy(convertPerson.convert(source.getClosedBy()));
|
||||||
|
}
|
||||||
|
|
||||||
|
issue.setAuthor(convertPerson.convert(source.getAuthor()));
|
||||||
|
return issue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertAssignees(Issue issue, List<PersonJson> jsonAssignees) {
|
||||||
|
if (checkNotEmpty(jsonAssignees)) {
|
||||||
|
final List<Person> assignees = jsonAssignees.stream()
|
||||||
|
.map(convertPerson::convert)
|
||||||
|
.toList();
|
||||||
|
issue.setAssignees(assignees);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convertLabels(Issue issue, Set<String> source) {
|
||||||
|
if (checkNotEmpty(source)) {
|
||||||
|
final Set<String> labels = source.stream()
|
||||||
|
.map(label -> label.replace("-", "_"))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
issue.setLabels(labels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IssueState convertState(IssueStateJson state) {
|
||||||
|
return switch (state) {
|
||||||
|
case CLOSED -> IssueState.CLOSED;
|
||||||
|
case OPENED -> IssueState.OPENED;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IssueType convertType(IssueTypeJson type) {
|
||||||
|
return switch (type) {
|
||||||
|
case ISSUE -> IssueType.ISSUE;
|
||||||
|
case INCIDENT -> IssueType.INCIDENT;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,313 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.core.service.impl;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.*;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Issue;
|
||||||
|
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.notify.issue.*;
|
||||||
|
import dev.struchkov.bot.gitlab.context.repository.IssueRepository;
|
||||||
|
import dev.struchkov.bot.gitlab.context.service.IssueService;
|
||||||
|
import dev.struchkov.bot.gitlab.context.service.NotifyService;
|
||||||
|
import dev.struchkov.bot.gitlab.context.service.ProjectService;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.domain.IssueState.CLOSED;
|
||||||
|
import static dev.struchkov.haiti.context.exception.NotFoundException.notFoundException;
|
||||||
|
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [25.01.2023]
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IssueServiceImpl implements IssueService {
|
||||||
|
|
||||||
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
||||||
|
public static final Set<IssueState> DELETE_STATES = Set.of(CLOSED);
|
||||||
|
|
||||||
|
private final PersonInformation personInformation;
|
||||||
|
private final IssueRepository repository;
|
||||||
|
private final ProjectService projectService;
|
||||||
|
private final NotifyService notifyService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Issue create(@NonNull Issue issue) {
|
||||||
|
final boolean botUserAssignee = isBotUserAssignee(issue);
|
||||||
|
final boolean botUserAssigneeAndNotAuthor = isBotUserAssigneeAndNotAuthor(issue);
|
||||||
|
issue.setUserAssignee(botUserAssignee);
|
||||||
|
issue.setNotification(botUserAssigneeAndNotAuthor);
|
||||||
|
|
||||||
|
final Issue savedIssue = repository.save(issue);
|
||||||
|
|
||||||
|
if (botUserAssigneeAndNotAuthor) {
|
||||||
|
final String projectName = projectService.getByIdOrThrow(savedIssue.getProjectId()).getName();
|
||||||
|
sendNotifyAboutAssignee(issue, projectName);
|
||||||
|
}
|
||||||
|
return savedIssue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBotUserAssignee(Issue savedIssue) {
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
final List<Person> assignees = savedIssue.getAssignees();
|
||||||
|
|
||||||
|
if (checkNotEmpty(assignees)) {
|
||||||
|
for (Person assignee : assignees) {
|
||||||
|
if (gitlabUserId.equals(assignee.getId())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBotUserAssigneeAndNotAuthor(Issue savedIssue) {
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
final boolean botUserAssignee = isBotUserAssignee(savedIssue);
|
||||||
|
|
||||||
|
if (botUserAssignee) {
|
||||||
|
return !gitlabUserId.equals(savedIssue.getAuthor().getId());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendNotifyAboutAssignee(Issue issue, String projectName) {
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
if (!gitlabUserId.equals(issue.getAuthor().getId()) // создатель Issue не является пользователем бота
|
||||||
|
)
|
||||||
|
notifyService.send(
|
||||||
|
NewIssueNotify.builder()
|
||||||
|
.projectName(projectName)
|
||||||
|
.title(issue.getTitle())
|
||||||
|
.url(issue.getWebUrl())
|
||||||
|
.issueType(issue.getType().name())
|
||||||
|
.author(issue.getAuthor().getName())
|
||||||
|
.description(issue.getDescription())
|
||||||
|
.dueDate(issue.getDueDate().format(DATE_FORMAT))
|
||||||
|
.labels(issue.getLabels())
|
||||||
|
.confidential(issue.getConfidential().toString())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendNotifyAboutDeleteFromAssignees(Issue issue, String projectName) {
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
if (!gitlabUserId.equals(issue.getAuthor().getId()) // создатель Issue не является пользователем бота
|
||||||
|
)
|
||||||
|
notifyService.send(
|
||||||
|
DeleteFromAssigneesNotify.builder()
|
||||||
|
.projectName(projectName)
|
||||||
|
.title(issue.getTitle())
|
||||||
|
.url(issue.getWebUrl())
|
||||||
|
.issueType(issue.getType().name())
|
||||||
|
.updateDate(issue.getUpdatedDate().format(DATE_FORMAT))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Issue update(@NonNull Issue issue) {
|
||||||
|
final Issue oldIssue = repository.findById(issue.getId())
|
||||||
|
.orElseThrow(notFoundException("Issue не найдено"));
|
||||||
|
|
||||||
|
issue.setNotification(oldIssue.isNotification());
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* проверяем изменения списка Assignees: пользователь появился в списке или удален из него или без изменений.
|
||||||
|
*/
|
||||||
|
final AssigneesChanged assigneesChanged = AssigneesChanged.valueOf(gitlabUserId, oldIssue.getAssignees(), issue.getAssignees());
|
||||||
|
issue.setUserAssignee(assigneesChanged.getNewStatus(oldIssue.isUserAssignee()));
|
||||||
|
final boolean isChangedIssue = !oldIssue.getUpdatedDate().equals(issue.getUpdatedDate());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Удаление пользователя из assignee не всегда обновляет UpdatedDate, поэтому добавляется
|
||||||
|
* второе условие assigneesChanged.isChanged()
|
||||||
|
*/
|
||||||
|
if (isChangedIssue || assigneesChanged.isChanged()) {
|
||||||
|
|
||||||
|
if (assigneesChanged.equals(AssigneesChanged.BECOME) && !gitlabUserId.equals(issue.getAuthor().getId()))
|
||||||
|
issue.setNotification(true);
|
||||||
|
|
||||||
|
if (issue.isNotification()) {
|
||||||
|
final Project project = projectService.getByIdOrThrow(issue.getProjectId());
|
||||||
|
notifyAboutStatus(oldIssue, issue, project);
|
||||||
|
notifyAboutType(oldIssue, issue, project);
|
||||||
|
notifyAboutTitle(oldIssue, issue, project);
|
||||||
|
notifyAboutDescription(oldIssue, issue, project);
|
||||||
|
notifyAboutDueDate(oldIssue, issue, project);
|
||||||
|
notifyAboutChangeAssignees(assigneesChanged, issue, project);
|
||||||
|
}
|
||||||
|
return repository.save(issue);
|
||||||
|
}
|
||||||
|
return oldIssue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExistContainer<Issue, Long> existsById(@NonNull Set<Long> issueIds) {
|
||||||
|
final List<Issue> existsEntity = repository.findAllById(issueIds);
|
||||||
|
final Set<Long> existsIds = existsEntity.stream().map(Issue::getId).collect(Collectors.toSet());
|
||||||
|
if (existsIds.containsAll(issueIds)) {
|
||||||
|
return ExistContainer.allFind(existsEntity);
|
||||||
|
} else {
|
||||||
|
final Set<Long> noExistsId = issueIds.stream()
|
||||||
|
.filter(id -> !existsIds.contains(id))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
return ExistContainer.notAllFind(existsEntity, noExistsId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Issue> createAll(List<Issue> newIssues) {
|
||||||
|
return newIssues.stream()
|
||||||
|
.map(this::create)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public List<Issue> updateAll(@NonNull List<Issue> issues) {
|
||||||
|
return issues.stream()
|
||||||
|
.map(this::update)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<IdAndStatusIssue> getAllId(Set<IssueState> statuses) {
|
||||||
|
return repository.findAllIdByStateIn(statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyAboutChangeAssignees(AssigneesChanged assigneesChanged, Issue issue, Project project) {
|
||||||
|
switch (assigneesChanged) {
|
||||||
|
case BECOME -> sendNotifyAboutAssignee(issue, project.getName());
|
||||||
|
case DELETED -> {
|
||||||
|
sendNotifyAboutDeleteFromAssignees(issue, project.getName());
|
||||||
|
issue.setUserAssignee(false);
|
||||||
|
issue.setNotification(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyAboutTitle(Issue oldIssue, Issue newIssue, Project project) {
|
||||||
|
final String oldTitle = oldIssue.getTitle();
|
||||||
|
final String newTitle = newIssue.getTitle();
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
if (
|
||||||
|
!oldTitle.equals(newTitle) // заголовок изменился
|
||||||
|
&& !gitlabUserId.equals(oldIssue.getAuthor().getId()) // создатель Issue не является пользователем бота
|
||||||
|
) {
|
||||||
|
notifyService.send(
|
||||||
|
TitleIssueNotify.builder()
|
||||||
|
.projectName(project.getName())
|
||||||
|
.title(oldIssue.getTitle())
|
||||||
|
.url(oldIssue.getWebUrl())
|
||||||
|
.issueType(oldIssue.getType().name())
|
||||||
|
.newTitle(newTitle)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyAboutDescription(Issue oldIssue, Issue newIssue, Project project) {
|
||||||
|
final String oldDescription = oldIssue.getDescription();
|
||||||
|
final String newDescription = newIssue.getDescription();
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
if (
|
||||||
|
!oldDescription.equals(newDescription) // описание изменилось
|
||||||
|
&& !gitlabUserId.equals(oldIssue.getAuthor().getId()) // создатель Issue не является пользователем бота
|
||||||
|
) {
|
||||||
|
notifyService.send(
|
||||||
|
DescriptionIssueNotify.builder()
|
||||||
|
.projectName(project.getName())
|
||||||
|
.title(oldIssue.getTitle())
|
||||||
|
.url(oldIssue.getWebUrl())
|
||||||
|
.issueType(oldIssue.getType().name())
|
||||||
|
.newDescription(newDescription)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyAboutType(Issue oldIssue, Issue newIssue, Project project) {
|
||||||
|
final IssueType oldType = oldIssue.getType();
|
||||||
|
final IssueType newType = newIssue.getType();
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
if (
|
||||||
|
!oldType.equals(newType) // тип изменился
|
||||||
|
&& !gitlabUserId.equals(oldIssue.getAuthor().getId()) // создатель Issue не является пользователем бота
|
||||||
|
) {
|
||||||
|
notifyService.send(
|
||||||
|
TypeIssueNotify.builder()
|
||||||
|
.projectName(project.getName())
|
||||||
|
.title(oldIssue.getTitle())
|
||||||
|
.url(oldIssue.getWebUrl())
|
||||||
|
.issueType(oldIssue.getType().name())
|
||||||
|
.oldType(oldType)
|
||||||
|
.newType(newType)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyAboutStatus(Issue oldIssue, Issue newIssue, Project project) {
|
||||||
|
final IssueState oldStatus = oldIssue.getState();
|
||||||
|
final IssueState newStatus = newIssue.getState();
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
if (
|
||||||
|
!oldStatus.equals(newStatus) // статус изменился
|
||||||
|
&& gitlabUserId.equals(oldIssue.getAuthor().getId()) // создатель Issue является пользователем бота
|
||||||
|
) {
|
||||||
|
notifyService.send(
|
||||||
|
StatusIssueNotify.builder()
|
||||||
|
.name(newIssue.getTitle())
|
||||||
|
.url(oldIssue.getWebUrl())
|
||||||
|
.issueType(oldIssue.getType().name())
|
||||||
|
.projectName(project.getName())
|
||||||
|
.newStatus(newStatus)
|
||||||
|
.oldStatus(oldStatus)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void notifyAboutDueDate(Issue oldIssue, Issue newIssue, Project project) {
|
||||||
|
final String oldDueDate = oldIssue.getDueDate().format(DATE_FORMAT);
|
||||||
|
final String newDueDate = newIssue.getDueDate().format(DATE_FORMAT);
|
||||||
|
final Long gitlabUserId = personInformation.getId();
|
||||||
|
if (
|
||||||
|
(!Objects.equals(oldDueDate, newDueDate)) // дата изменилась
|
||||||
|
&& (!gitlabUserId.equals(oldIssue.getAuthor().getId())) // создатель Issue не является пользователем бота
|
||||||
|
) {
|
||||||
|
notifyService.send(
|
||||||
|
DueDateIssueNotify.builder()
|
||||||
|
.projectName(project.getName())
|
||||||
|
.title(oldIssue.getTitle())
|
||||||
|
.url(oldIssue.getWebUrl())
|
||||||
|
.issueType(oldIssue.getType().name())
|
||||||
|
.oldDueDate(oldDueDate)
|
||||||
|
.newDueDate(newDueDate)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanOld() {
|
||||||
|
log.debug("Старт очистки старых Issue");
|
||||||
|
repository.deleteByStates(DELETE_STATES);
|
||||||
|
log.debug("Конец очистки старых Issue");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.core.service.parser;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.*;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Issue;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Person;
|
||||||
|
import dev.struchkov.bot.gitlab.context.service.IssueService;
|
||||||
|
import dev.struchkov.bot.gitlab.context.service.ProjectService;
|
||||||
|
import dev.struchkov.bot.gitlab.core.config.properties.GitlabProperty;
|
||||||
|
import dev.struchkov.bot.gitlab.core.config.properties.PersonProperty;
|
||||||
|
import dev.struchkov.bot.gitlab.core.service.parser.forktask.GetAllIssueForProjectTask;
|
||||||
|
import dev.struchkov.bot.gitlab.core.service.parser.forktask.GetSingleIssueTask;
|
||||||
|
import dev.struchkov.bot.gitlab.sdk.domain.IssueJson;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
import java.util.concurrent.ForkJoinTask;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
|
||||||
|
import static dev.struchkov.haiti.utils.concurrent.ForkJoinUtils.pullTaskResult;
|
||||||
|
import static dev.struchkov.haiti.utils.concurrent.ForkJoinUtils.pullTaskResults;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmotry Sheyko [24.01.2023]
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class IssueParser {
|
||||||
|
|
||||||
|
private static final Set<IssueState> OLD_STATUSES = Set.of(IssueState.OPENED, IssueState.CLOSED);
|
||||||
|
|
||||||
|
private final GitlabProperty gitlabProperty;
|
||||||
|
private final IssueService issueService;
|
||||||
|
private final ProjectService projectService;
|
||||||
|
private final ConversionService conversionService;
|
||||||
|
private final PersonProperty personProperty;
|
||||||
|
|
||||||
|
private final ForkJoinPool forkJoinPool;
|
||||||
|
|
||||||
|
public IssueParser(
|
||||||
|
GitlabProperty gitlabProperty,
|
||||||
|
IssueService issueService,
|
||||||
|
ProjectService projectService,
|
||||||
|
ConversionService conversionService,
|
||||||
|
PersonProperty personProperty,
|
||||||
|
@Qualifier("parserPool") ForkJoinPool forkJoinPool
|
||||||
|
) {
|
||||||
|
this.gitlabProperty = gitlabProperty;
|
||||||
|
this.issueService = issueService;
|
||||||
|
this.projectService = projectService;
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
this.personProperty = personProperty;
|
||||||
|
this.forkJoinPool = forkJoinPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parsingOldIssue(){
|
||||||
|
log.debug("Старт обработаки старых Issue");
|
||||||
|
final Set<IdAndStatusIssue> existIds = issueService.getAllId(OLD_STATUSES);
|
||||||
|
|
||||||
|
final List<Issue> newIssues = getOldIssues(existIds).stream()
|
||||||
|
.map(issueJson -> {
|
||||||
|
final Issue newIssue = conversionService.convert(issueJson, Issue.class);
|
||||||
|
return newIssue;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (checkNotEmpty(newIssues)) {
|
||||||
|
personMapping(newIssues);
|
||||||
|
issueService.updateAll(newIssues);
|
||||||
|
}
|
||||||
|
log.debug("Конец обработки старых Issue");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IssueJson> getOldIssues(Set<IdAndStatusIssue> existIds) {
|
||||||
|
final List<ForkJoinTask<Optional<IssueJson>>> tasks = existIds.stream()
|
||||||
|
.map(
|
||||||
|
existId -> new GetSingleIssueTask(
|
||||||
|
gitlabProperty.getIssueUrl(),
|
||||||
|
existId.getProjectId(),
|
||||||
|
existId.getTwoId(),
|
||||||
|
personProperty.getToken()
|
||||||
|
)
|
||||||
|
).map(forkJoinPool::submit)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return pullTaskResult(tasks).stream()
|
||||||
|
.flatMap(Optional::stream)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void parsingNewIssue() {
|
||||||
|
log.debug("Старт обработки новых Issue");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* получаем через репозиторий список id всех проектов хранящихся в БД
|
||||||
|
*/
|
||||||
|
final Set<Long> projectIds = projectService.getAllIds();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* На основе id проекта, url для получения issues по id проекта и токена пользователя
|
||||||
|
* выгружаем из GitLab список всех IssueJson. Получаем в многопоточном режиме.
|
||||||
|
*/
|
||||||
|
final List<IssueJson> issueJsons = getIssues(projectIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получаем id всех IssueJson загруженных из GitLab
|
||||||
|
*/
|
||||||
|
if (checkNotEmpty(issueJsons)) {
|
||||||
|
final Set<Long> jsonIds = issueJsons.stream()
|
||||||
|
.map(IssueJson::getId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
final ExistContainer<Issue, Long> existContainer = issueService.existsById(jsonIds);
|
||||||
|
log.trace("Из {} полученных MR не найдены в хранилище {}", jsonIds.size(), existContainer.getIdNoFound().size());
|
||||||
|
if (!existContainer.isAllFound()) {
|
||||||
|
final List<Issue> newIssues = issueJsons.stream()
|
||||||
|
.filter(json -> existContainer.getIdNoFound().contains(json.getId()))
|
||||||
|
.map(json -> {
|
||||||
|
final Issue issue = conversionService.convert(json, Issue.class);
|
||||||
|
return issue;
|
||||||
|
})
|
||||||
|
.toList();
|
||||||
|
log.trace("Пачка новых issues обработана и отправлена на сохранение. Количество: {} шт.", newIssues.size());
|
||||||
|
issueService.createAll(newIssues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.debug("Конец обработки новых MR");
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IssueJson> getIssues(Set<Long> projectIds) {
|
||||||
|
final List<ForkJoinTask<List<IssueJson>>> tasks = projectIds.stream()
|
||||||
|
.map(projectId -> new GetAllIssueForProjectTask(projectId, gitlabProperty.getOpenIssueUrl(), personProperty.getToken()))
|
||||||
|
.map(forkJoinPool::submit)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return pullTaskResults(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void personMapping(List<Issue> newIssues) {
|
||||||
|
final Map<Long, Person> personMap = Stream.concat(
|
||||||
|
newIssues.stream()
|
||||||
|
.map(Issue::getAuthor),
|
||||||
|
newIssues.stream()
|
||||||
|
.flatMap(issue -> issue.getAssignees().stream())
|
||||||
|
).distinct()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Person::getId, p -> p));
|
||||||
|
|
||||||
|
for (Issue newIssue : newIssues) {
|
||||||
|
newIssue.setAuthor(personMap.get(newIssue.getAuthor().getId()));
|
||||||
|
|
||||||
|
newIssue.setAssignees(
|
||||||
|
newIssue.getAssignees().stream()
|
||||||
|
.map(reviewer -> personMap.get(reviewer.getId()))
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.core.service.parser.forktask;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.core.utils.StringUtils;
|
||||||
|
import dev.struchkov.bot.gitlab.sdk.domain.IssueJson;
|
||||||
|
import dev.struchkov.haiti.utils.network.HttpParse;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.RecursiveTask;
|
||||||
|
|
||||||
|
import static dev.struchkov.haiti.utils.Checker.checkNotEmpty;
|
||||||
|
import static dev.struchkov.haiti.utils.network.HttpParse.ACCEPT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [24.01.2023]
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GetAllIssueForProjectTask extends RecursiveTask<List<IssueJson>> {
|
||||||
|
|
||||||
|
private static final int PAGE_COUNT = 100;
|
||||||
|
|
||||||
|
private final long projectId;
|
||||||
|
private int pageNumber = 1;
|
||||||
|
private final String urlIssueOpen;
|
||||||
|
private final String gitlabToken;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
protected List<IssueJson> compute() {
|
||||||
|
Thread.sleep(200);
|
||||||
|
final List<IssueJson> issueJson = getIssueJsons();
|
||||||
|
if (checkNotEmpty(issueJson) && issueJson.size() == PAGE_COUNT) {
|
||||||
|
final GetAllIssueForProjectTask newTask = new GetAllIssueForProjectTask(projectId, pageNumber + 1, urlIssueOpen, gitlabToken);
|
||||||
|
newTask.fork();
|
||||||
|
issueJson.addAll(newTask.join());
|
||||||
|
}
|
||||||
|
return issueJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IssueJson> getIssueJsons() {
|
||||||
|
final List<IssueJson> jsons = HttpParse.request(MessageFormat.format(urlIssueOpen, projectId, pageNumber, PAGE_COUNT))
|
||||||
|
.header(StringUtils.H_PRIVATE_TOKEN, gitlabToken)
|
||||||
|
.header(ACCEPT)
|
||||||
|
.executeList(IssueJson.class);
|
||||||
|
log.trace("Получено {} шт потенциально новых Issue для проекта id:'{}' ", jsons.size(), projectId);
|
||||||
|
return jsons;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.core.service.parser.forktask;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.core.utils.StringUtils;
|
||||||
|
import dev.struchkov.bot.gitlab.sdk.domain.IssueJson;
|
||||||
|
import dev.struchkov.haiti.utils.network.HttpParse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.RecursiveTask;
|
||||||
|
|
||||||
|
import static dev.struchkov.haiti.utils.network.HttpParse.ACCEPT;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GetSingleIssueTask extends RecursiveTask<Optional<IssueJson>> {
|
||||||
|
|
||||||
|
private final String urlIssue;
|
||||||
|
private final long projectId;
|
||||||
|
private final long issueTwoId;
|
||||||
|
private final String gitlabToken;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SneakyThrows
|
||||||
|
protected Optional<IssueJson> compute() {
|
||||||
|
Thread.sleep(200);
|
||||||
|
final String mrUrl = MessageFormat.format(urlIssue, projectId, issueTwoId);
|
||||||
|
return HttpParse.request(mrUrl)
|
||||||
|
.header(ACCEPT)
|
||||||
|
.header(StringUtils.H_PRIVATE_TOKEN, gitlabToken)
|
||||||
|
.execute(IssueJson.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.data.impl;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IdAndStatusIssue;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IssueState;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Issue;
|
||||||
|
import dev.struchkov.bot.gitlab.context.repository.IssueRepository;
|
||||||
|
import dev.struchkov.bot.gitlab.data.jpa.IssueJpaRepository;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [24.01.2023]
|
||||||
|
*/
|
||||||
|
@Repository
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class IssueRepositoryImpl implements IssueRepository {
|
||||||
|
|
||||||
|
private final IssueJpaRepository jpaRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Set<IdAndStatusIssue> findAllIdByStateIn(@NonNull Set<IssueState> statuses) {
|
||||||
|
return jpaRepository.findAllIdByStateIn(statuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Issue save(Issue issue) {
|
||||||
|
return jpaRepository.save(issue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Optional<Issue> findById(Long issueId) {
|
||||||
|
return jpaRepository.findById(issueId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<Issue> findAllById(Set<Long> issueIds) {
|
||||||
|
return jpaRepository.findAllById(issueIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void deleteByStates(Set<IssueState> states) {
|
||||||
|
jpaRepository.deleteAllByStateIn(states);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.data.jpa;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IdAndStatusIssue;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.IssueState;
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.entity.Issue;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko [24.01.2023]
|
||||||
|
*/
|
||||||
|
public interface IssueJpaRepository extends JpaRepository<Issue, Long> {
|
||||||
|
|
||||||
|
@Query("SELECT new dev.struchkov.bot.gitlab.context.domain.IdAndStatusIssue(i.id, i.twoId, i.projectId, i.state) FROM Issue i WHERE i.state IN :states")
|
||||||
|
Set<IdAndStatusIssue> findAllIdByStateIn(@Param("states") Set<IssueState> states);
|
||||||
|
|
||||||
|
void deleteAllByStateIn(Set<IssueState> states);
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package dev.struchkov.bot.gitlab.scheduler;
|
package dev.struchkov.bot.gitlab.scheduler;
|
||||||
|
|
||||||
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
|
import dev.struchkov.bot.gitlab.context.service.AppSettingService;
|
||||||
|
import dev.struchkov.bot.gitlab.context.service.IssueService;
|
||||||
import dev.struchkov.bot.gitlab.context.service.MergeRequestsService;
|
import dev.struchkov.bot.gitlab.context.service.MergeRequestsService;
|
||||||
import dev.struchkov.bot.gitlab.context.service.PipelineService;
|
import dev.struchkov.bot.gitlab.context.service.PipelineService;
|
||||||
import dev.struchkov.bot.gitlab.core.service.parser.DiscussionParser;
|
import dev.struchkov.bot.gitlab.core.service.parser.DiscussionParser;
|
||||||
|
import dev.struchkov.bot.gitlab.core.service.parser.IssueParser;
|
||||||
import dev.struchkov.bot.gitlab.core.service.parser.MergeRequestParser;
|
import dev.struchkov.bot.gitlab.core.service.parser.MergeRequestParser;
|
||||||
import dev.struchkov.bot.gitlab.core.service.parser.PipelineParser;
|
import dev.struchkov.bot.gitlab.core.service.parser.PipelineParser;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -28,6 +30,9 @@ public class SchedulerService {
|
|||||||
private final PipelineService pipelineService;
|
private final PipelineService pipelineService;
|
||||||
private final MergeRequestsService mergeRequestsService;
|
private final MergeRequestsService mergeRequestsService;
|
||||||
|
|
||||||
|
private final IssueParser issueParser;
|
||||||
|
private final IssueService issueService;
|
||||||
|
|
||||||
@Scheduled(cron = "0 */1 * * * *")
|
@Scheduled(cron = "0 */1 * * * *")
|
||||||
public void newMergeRequest() {
|
public void newMergeRequest() {
|
||||||
log.debug("Запуск процесса обновления данных");
|
log.debug("Запуск процесса обновления данных");
|
||||||
@ -40,6 +45,9 @@ public class SchedulerService {
|
|||||||
discussionParser.scanNewDiscussion();
|
discussionParser.scanNewDiscussion();
|
||||||
mergeRequestsService.cleanOld();
|
mergeRequestsService.cleanOld();
|
||||||
pipelineService.cleanOld();
|
pipelineService.cleanOld();
|
||||||
|
issueParser.parsingNewIssue();
|
||||||
|
issueParser.parsingOldIssue();
|
||||||
|
issueService.cleanOld();
|
||||||
} else {
|
} else {
|
||||||
log.warn("Процесс обновления данных не был выполнен, так как пользователь не выполнил первичную настройку.");
|
log.warn("Процесс обновления данных не был выполнен, так как пользователь не выполнил первичную настройку.");
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ spring:
|
|||||||
jdbc:
|
jdbc:
|
||||||
lob:
|
lob:
|
||||||
non_contextual_creation: true
|
non_contextual_creation: true
|
||||||
|
event:
|
||||||
|
merge.entity_copy_observer: allow
|
||||||
telegram-config:
|
telegram-config:
|
||||||
bot-username: ${TELEGRAM_BOT_USERNAME}
|
bot-username: ${TELEGRAM_BOT_USERNAME}
|
||||||
bot-token: ${TELEGRAM_BOT_TOKEN}
|
bot-token: ${TELEGRAM_BOT_TOKEN}
|
||||||
@ -51,3 +53,5 @@ gitlab-bot:
|
|||||||
new-note-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/discussions/{2}/notes?body={3}"
|
new-note-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/discussions/{2}/notes?body={3}"
|
||||||
discussions-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/discussions?&page={2,number,#}&per_page={3,number,#}"
|
discussions-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/discussions?&page={2,number,#}&per_page={3,number,#}"
|
||||||
discussion-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/discussions/{2}"
|
discussion-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/discussions/{2}"
|
||||||
|
issue-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/issues/{1,number,#}"
|
||||||
|
open-issue-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/issues?state=opened&page={1, number, integer}&per_page={2, number, integer}"
|
@ -77,6 +77,12 @@
|
|||||||
<column name="has_tasks" type="boolean">
|
<column name="has_tasks" type="boolean">
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
|
<column name="notification" type="boolean">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
|
<column name="is_assignee" type="boolean">
|
||||||
|
<constraints nullable="false"/>
|
||||||
|
</column>
|
||||||
</createTable>
|
</createTable>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
||||||
|
@ -3,11 +3,15 @@ package dev.struchkov.bot.gitlab.sdk.domain;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +51,7 @@ public class IssueJson {
|
|||||||
|
|
||||||
private Set<String> labels;
|
private Set<String> labels;
|
||||||
private MilestoneJson milestone;
|
private MilestoneJson milestone;
|
||||||
private Set<PersonJson> assignees;
|
private List<PersonJson> assignees;
|
||||||
private PersonJson author;
|
private PersonJson author;
|
||||||
private IssueTypeJson type;
|
private IssueTypeJson type;
|
||||||
private PersonJson assignee;
|
private PersonJson assignee;
|
||||||
@ -64,10 +68,10 @@ public class IssueJson {
|
|||||||
@JsonProperty("downvotes")
|
@JsonProperty("downvotes")
|
||||||
private Integer downVotes;
|
private Integer downVotes;
|
||||||
|
|
||||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
@JsonSerialize(using = LocalDateSerializer.class)
|
||||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
@JsonDeserialize(using = LocalDateDeserializer.class)
|
||||||
@JsonProperty("due_date")
|
@JsonProperty("due_date")
|
||||||
private LocalDateTime dueDate;
|
private LocalDate dueDate;
|
||||||
private Boolean confidential;
|
private Boolean confidential;
|
||||||
|
|
||||||
@JsonProperty("discussion_locked")
|
@JsonProperty("discussion_locked")
|
||||||
|
@ -3,10 +3,13 @@ package dev.struchkov.bot.gitlab.sdk.domain;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,15 +39,15 @@ public class MilestoneJson {
|
|||||||
@JsonProperty("updated_at")
|
@JsonProperty("updated_at")
|
||||||
private LocalDateTime updatedDate;
|
private LocalDateTime updatedDate;
|
||||||
|
|
||||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
@JsonSerialize(using = LocalDateSerializer.class)
|
||||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
@JsonDeserialize(using = LocalDateDeserializer.class)
|
||||||
@JsonProperty("start_date")
|
@JsonProperty("start_date")
|
||||||
private LocalDateTime startDate;
|
private LocalDate startDate;
|
||||||
|
|
||||||
@JsonSerialize(using = LocalDateTimeSerializer.class)
|
@JsonSerialize(using = LocalDateSerializer.class)
|
||||||
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
|
@JsonDeserialize(using = LocalDateDeserializer.class)
|
||||||
@JsonProperty("due_date")
|
@JsonProperty("due_date")
|
||||||
private LocalDateTime dueDate;
|
private LocalDate dueDate;
|
||||||
private boolean expired;
|
private boolean expired;
|
||||||
|
|
||||||
@JsonProperty("web_url")
|
@JsonProperty("web_url")
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.telegram.service.notify;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.issue.*;
|
||||||
|
import dev.struchkov.bot.gitlab.context.utils.Icons;
|
||||||
|
import dev.struchkov.godfather.main.domain.BoxAnswer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
|
||||||
|
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 26.01.2021
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DeleteFromAssigneesOfIssueNotifyGenerator implements NotifyBoxAnswerGenerator<DeleteFromAssigneesNotify> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoxAnswer generate(DeleteFromAssigneesNotify notify) {
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder(Icons.PEN)
|
||||||
|
.append(String.format(" *You excluded from %s assignees | ", notify.getIssueType()))
|
||||||
|
.append(notify.getProjectName()).append("*")
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(link(notify.getType(), notify.getUrl()))
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(notify.getUpdateDate());
|
||||||
|
final String notifyMessage = builder.toString();
|
||||||
|
|
||||||
|
return boxAnswer(notifyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotifyType() {
|
||||||
|
return DeleteFromAssigneesNotify.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.telegram.service.notify;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.issue.DescriptionIssueNotify;
|
||||||
|
import dev.struchkov.bot.gitlab.context.utils.Icons;
|
||||||
|
import dev.struchkov.godfather.main.domain.BoxAnswer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
|
||||||
|
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 26.01.2021
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DescriptionIssueNotifyGenerator implements NotifyBoxAnswerGenerator<DescriptionIssueNotify> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoxAnswer generate(DescriptionIssueNotify notify) {
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder(Icons.PEN)
|
||||||
|
.append(String.format(" *Description of %s changed | ", notify.getIssueType()))
|
||||||
|
.append(notify.getProjectName()).append("*")
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(link(notify.getType(), notify.getUrl()))
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append("new description: ")
|
||||||
|
.append(notify.getNewDescription());
|
||||||
|
|
||||||
|
final String notifyMessage = builder.toString();
|
||||||
|
|
||||||
|
return boxAnswer(notifyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotifyType() {
|
||||||
|
return DescriptionIssueNotify.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.telegram.service.notify;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.issue.DueDateIssueNotify;
|
||||||
|
import dev.struchkov.bot.gitlab.context.utils.Icons;
|
||||||
|
import dev.struchkov.godfather.main.domain.BoxAnswer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
|
||||||
|
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 26.01.2021
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DueDateIssueNotifyGenerator implements NotifyBoxAnswerGenerator<DueDateIssueNotify> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoxAnswer generate(DueDateIssueNotify notify) {
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder(Icons.PEN)
|
||||||
|
.append(String.format(" *Due date of %s changed | ", notify.getIssueType()))
|
||||||
|
.append(notify.getProjectName()).append("*")
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(link(notify.getType(), notify.getUrl()))
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(notify.getOldDueDate()).append(Icons.ARROW).append(notify.getNewDueDate());
|
||||||
|
|
||||||
|
final String notifyMessage = builder.toString();
|
||||||
|
|
||||||
|
return boxAnswer(notifyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotifyType() {
|
||||||
|
return DueDateIssueNotify.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.telegram.service.notify;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.issue.NewIssueNotify;
|
||||||
|
import dev.struchkov.bot.gitlab.context.utils.Icons;
|
||||||
|
import dev.struchkov.godfather.main.domain.BoxAnswer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
|
||||||
|
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
|
||||||
|
import static dev.struchkov.haiti.utils.Strings.escapeMarkdown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 24.01.2023
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class NewIssueNotifyGenerator implements NotifyBoxAnswerGenerator<NewIssueNotify> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoxAnswer generate(NewIssueNotify notify) {
|
||||||
|
final String labelText = notify.getLabels().stream()
|
||||||
|
.map(label -> "#" + label)
|
||||||
|
.collect(Collectors.joining(" "));
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder(Icons.FUN)
|
||||||
|
.append(String.format(" *New %s assigned to you | ", notify.getIssueType()))
|
||||||
|
.append(escapeMarkdown(notify.getProjectName())).append("*")
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(link(notify.getType(), notify.getUrl()));
|
||||||
|
|
||||||
|
if (!labelText.isEmpty()) {
|
||||||
|
builder.append("\n\n").append(labelText);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(Icons.HR)
|
||||||
|
.append(Icons.BELL).append(": ").append(notify.getTitle()).append("\n")
|
||||||
|
.append(Icons.AUTHOR).append(": ").append(notify.getAuthor());
|
||||||
|
|
||||||
|
final String notifyMessage = builder.toString();
|
||||||
|
return boxAnswer(notifyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotifyType() {
|
||||||
|
return NewIssueNotify.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.telegram.service.notify;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.issue.StatusIssueNotify;
|
||||||
|
import dev.struchkov.bot.gitlab.context.utils.Icons;
|
||||||
|
import dev.struchkov.godfather.main.domain.BoxAnswer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
|
||||||
|
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 26.01.2021
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class StatusIssueNotifyGenerator implements NotifyBoxAnswerGenerator<StatusIssueNotify> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoxAnswer generate(StatusIssueNotify notify) {
|
||||||
|
final StringBuilder builder = new StringBuilder(Icons.PEN)
|
||||||
|
.append(String.format(" *Status of %s changed | ", notify.getIssueType()))
|
||||||
|
.append(notify.getProjectName()).append("*")
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(link(notify.getType(), notify.getUrl()))
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(notify.getOldStatus().name()).append(Icons.ARROW).append(notify.getNewStatus().name());
|
||||||
|
|
||||||
|
|
||||||
|
final String notifyMessage = builder.toString();
|
||||||
|
|
||||||
|
return boxAnswer(notifyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotifyType() {
|
||||||
|
return StatusIssueNotify.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.telegram.service.notify;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.issue.TitleIssueNotify;
|
||||||
|
import dev.struchkov.bot.gitlab.context.utils.Icons;
|
||||||
|
import dev.struchkov.godfather.main.domain.BoxAnswer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
|
||||||
|
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 26.01.2021
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class TitleIssueNotifyGenerator implements NotifyBoxAnswerGenerator<TitleIssueNotify> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoxAnswer generate(TitleIssueNotify notify) {
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder(Icons.PEN)
|
||||||
|
.append(String.format(" *Title of %s changed | ", notify.getIssueType()))
|
||||||
|
.append(notify.getProjectName()).append("*")
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(link(notify.getType(), notify.getUrl()))
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append("new title: ")
|
||||||
|
.append(notify.getNewTitle());
|
||||||
|
|
||||||
|
final String notifyMessage = builder.toString();
|
||||||
|
|
||||||
|
return boxAnswer(notifyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotifyType() {
|
||||||
|
return TitleIssueNotify.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package dev.struchkov.bot.gitlab.telegram.service.notify;
|
||||||
|
|
||||||
|
import dev.struchkov.bot.gitlab.context.domain.notify.issue.TypeIssueNotify;
|
||||||
|
import dev.struchkov.bot.gitlab.context.utils.Icons;
|
||||||
|
import dev.struchkov.godfather.main.domain.BoxAnswer;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import static dev.struchkov.bot.gitlab.context.utils.Icons.link;
|
||||||
|
import static dev.struchkov.godfather.main.domain.BoxAnswer.boxAnswer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Dmitry Sheyko 26.01.2021
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class TypeIssueNotifyGenerator implements NotifyBoxAnswerGenerator<TypeIssueNotify> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BoxAnswer generate(TypeIssueNotify notify) {
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder(Icons.PEN)
|
||||||
|
.append(String.format(" *Type of %s changed | ", notify.getIssueType()))
|
||||||
|
.append(notify.getProjectName()).append("*")
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(link(notify.getType(), notify.getUrl()))
|
||||||
|
.append(Icons.HR)
|
||||||
|
.append(notify.getOldType().name()).append(Icons.ARROW).append(notify.getNewType().name());
|
||||||
|
|
||||||
|
final String notifyMessage = builder.toString();
|
||||||
|
|
||||||
|
return boxAnswer(notifyMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNotifyType() {
|
||||||
|
return TypeIssueNotify.TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user