From f1461650245765ee0edd22db8ad79cb2a0e00648 Mon Sep 17 00:00:00 2001 From: DmitrySheyko Date: Wed, 18 Jan 2023 21:52:25 +0300 Subject: [PATCH 1/5] =?UTF-8?q?Feat:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82=D1=8C?= =?UTF-8?q?=20Issue=20=D0=B8=20=D0=B2=D1=81=D0=BF=D0=BE=D0=BC=D0=BE=D0=B3?= =?UTF-8?q?=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=B5,=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=D1=81=D1=82=D1=83=D0=BF=D0=B0=D1=8E=20=D0=BA=20?= =?UTF-8?q?=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=D0=BC=20=D0=91?= =?UTF-8?q?=D0=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bot/gitlab/context/domain/IssueState.java | 5 + .../bot/gitlab/context/domain/IssueType.java | 6 + .../gitlab/context/domain/MilestoneState.java | 5 + .../gitlab/context/domain/entity/Issue.java | 181 ++++++++++++++++++ .../gitlab/context/domain/entity/Links.java | 23 +++ .../context/domain/entity/Milestone.java | 64 +++++++ .../context/domain/entity/References.java | 21 ++ .../domain/entity/TaskCompletionStatus.java | 20 ++ .../context/domain/entity/TimeStats.java | 23 +++ .../service/parser/IssueRequestParser.java | 10 + .../bot/gitlab/sdk/domain/IssueJson.java | 108 +++++++++++ .../bot/gitlab/sdk/domain/IssueStateJson.java | 15 ++ .../bot/gitlab/sdk/domain/IssueTypeJson.java | 6 + .../bot/gitlab/sdk/domain/LinksJson.java | 21 ++ .../bot/gitlab/sdk/domain/MilestoneJson.java | 52 +++++ .../gitlab/sdk/domain/MilestoneStateJson.java | 15 ++ .../bot/gitlab/sdk/domain/ReferencesJson.java | 20 ++ .../sdk/domain/TaskCompletionStatusJson.java | 17 ++ .../bot/gitlab/sdk/domain/TimeStatsJson.java | 15 ++ 19 files changed, 627 insertions(+) create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java create mode 100644 bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/LinksJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TaskCompletionStatusJson.java create mode 100644 gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java new file mode 100644 index 0000000..2b86c09 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java @@ -0,0 +1,5 @@ +package dev.struchkov.bot.gitlab.context.domain; + +public enum IssueState { + OPENED, CLOSED +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java new file mode 100644 index 0000000..62bb1e5 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java @@ -0,0 +1,6 @@ +package dev.struchkov.bot.gitlab.context.domain; + +public enum IssueType { + ISSUE, + INCIDENT +} diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java new file mode 100644 index 0000000..4c0fb32 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java @@ -0,0 +1,5 @@ +package dev.struchkov.bot.gitlab.context.domain; + +public enum MilestoneState { + ACTIVE, CLOSED +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java new file mode 100644 index 0000000..88aad0c --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java @@ -0,0 +1,181 @@ +package dev.struchkov.bot.gitlab.context.domain.entity; + +import dev.struchkov.bot.gitlab.context.domain.IssueState; +import dev.struchkov.bot.gitlab.context.domain.IssueType; +import dev.struchkov.haiti.utils.fieldconstants.annotation.FieldNames; +import dev.struchkov.haiti.utils.fieldconstants.domain.Mode; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.Set; + + +/** + * Сущность Issue. + * + * @author Dmitry Sheyko [17.01.2023] + */ + +// При запросе issue учесть что пагинация по умолчанию - 20 объектов +@Getter +@Setter +@Entity +@FieldNames(mode = {Mode.TABLE, Mode.SIMPLE}) +@Table(name = "issue") +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class Issue { + + @Id + @Column(name = "id") + @EqualsAndHashCode.Include + private Long id; + + @Column(name = "iid") + private Long twoId; + + @Column(name = "project_id") + private Long projectId; + + @Column(name = "title") + private String title; + + @Column(name = "description") + private String description; + + @Enumerated(value = EnumType.STRING) + @Column(name = "state") + private IssueState state; + + @Column(name = "created_date") + private LocalDateTime createdDate; + + @Column(name = "updated_date") + private LocalDateTime updatedDate; + + @Column(name = "closed_at") + private LocalDateTime closeDate; + + @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinColumn(name = "closed_by_id") + private Person closedBy; + + @ElementCollection + @CollectionTable(name = "issue_label", joinColumns = @JoinColumn(name = "label_id")) + @Column(name = "labels") + private Set labels = new HashSet<>(); + + @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinColumn(name = "milestone_id") + private Milestone milestone; + + @Column(name = "assignees") + @OneToMany( + fetch = FetchType.LAZY, + cascade = {CascadeType.PERSIST, CascadeType.MERGE} + ) + @JoinTable( + name = "issue_assignees", + joinColumns = @JoinColumn(name = "issue_id", referencedColumnName = "id"), + inverseJoinColumns = @JoinColumn(name = "person_id", referencedColumnName = "id") + ) + private Set assignees = new HashSet<>(); + + @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinColumn(name = "author_id") + private Person author; + + @Enumerated(value = EnumType.STRING) + @Column(name = "type") + private IssueType type; // ОБразец приходящего значения "INCIDENT" + + @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinColumn(name = "assignee") + private Person assignee; + + @Column(name = "user_notes_count") //Количество комментов пользователя + private Integer userNotesCount; + + @Column(name = "merge_requests_count") + private Integer mergeRequestsCount; + + @Column(name = "upvotes") // Количество лайков + private Integer upVotes; + + @Column(name = "downvotes") // Количество дизлайков + private Integer downVotes; + + @Column(name = "due_date") + private LocalDateTime dueDate; + + @Column(name = "confidential") // Конфиденцальное или нет + private Boolean confidential; + + @Column(name = "discussion_locked") + private Integer discussionLocked; //TODO выяснить тип поляя. + + @Column(name = "issue_type") + private String issueType; //TODO выяснить зачем дублирует поле type Образец приходящего значения "incident" + + @Column(name = "web_url") + private String webUrl; + + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "timeEstimate", column = @Column(name = "time_estimate")), + @AttributeOverride(name = "totalTimeSpent", column = @Column(name = "total_time_spent")), + @AttributeOverride(name = "humanTimeEstimate", column = @Column(name = "human_time_estimate")), + @AttributeOverride(name = "humanTotalTimeSpent", column = @Column(name = "human_total_time_spent")) + }) + private TimeStats timeStats; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "count", column = @Column(name = "count")), + @AttributeOverride(name = "completedCount", column = @Column(name = "completed_count")) + }) + private TaskCompletionStatus taskCompletionStatus; + + @Column(name = "blocking_issues_count") + private Integer blockingIssuesCount; + + @Column(name = "has_tasks") + private Boolean hasTasks; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "self", column = @Column(name = "self")), + @AttributeOverride(name = "notes", column = @Column(name = "notes")), + @AttributeOverride(name = "awardEmoji", column = @Column(name = "award_emoji")), + @AttributeOverride(name = "project", column = @Column(name = "project")), + @AttributeOverride(name = "closedAsDuplicateOf", column = @Column(name = "closed_as_duplicate_of")) + }) + private Links links; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name = "shortReference", column = @Column(name = "short_reference")), + @AttributeOverride(name = "relativeReference", column = @Column(name = "relative_reference")), + @AttributeOverride(name = "fullReference", column = @Column(name = "full_reference")) + }) + private References references; + + /*Возможно надо заменить на енум "UNKNOWN", Critical - S1, High - S2, Medium - S3, Low - S4. + Но это поле доступно только для премиум акаунтов + */ + @Column(name = "severity") + private String severity; + + @Column(name = "moved_to_id") + private Long movedToId; + + @Column(name = "service_desk_reply_to") + private String serviceDescReplyTo; //TODO не понятен тип поля + + @Column(name = "epic_issue_id") + private Long epicId; // "epic_issue_id" Поле доснтупное только для премиум акаунтов +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java new file mode 100644 index 0000000..0537369 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java @@ -0,0 +1,23 @@ +package dev.struchkov.bot.gitlab.context.domain.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Embeddable; + +/** + * Сущность Issue. + * + * @author Dmitry Sheyko [17.01.2023] + */ + +@Embeddable +@Getter +@Setter +public class Links { + private String self; + private String notes; + private String awardEmoji; + private String project; + private String closedAsDuplicateOf; +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java new file mode 100644 index 0000000..3bde4d5 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java @@ -0,0 +1,64 @@ +package dev.struchkov.bot.gitlab.context.domain.entity; + +import dev.struchkov.bot.gitlab.context.domain.MilestoneState; +import dev.struchkov.haiti.utils.fieldconstants.annotation.FieldNames; +import dev.struchkov.haiti.utils.fieldconstants.domain.Mode; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.*; +import java.time.LocalDateTime; + +/** + * Сущность Milestone. + * + * @author Dmitry Sheyko 17.01.2023 + */ + +@Entity +@Getter +@Setter +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Table(name = "milestone") +@FieldNames(mode = {Mode.TABLE, Mode.SIMPLE}) +public class Milestone { + + @Id + @EqualsAndHashCode.Include + private Long id; + + @Column(name = "iid") + private Long twoId; + + @Column(name = "project_id") + private Long projectId; + + @Column(name = "title") + private String title; + + @Column(name = "description") + private String description; + + @Enumerated(value = EnumType.STRING) + @Column(name = "state") + private MilestoneState state; + + @Column(name = "created_date") + private LocalDateTime createdDate; + + @Column(name = "updated_date") + private LocalDateTime updatedDate; + + @Column(name = "start_date") + private LocalDateTime startDate; //установленное создателем время начала + + @Column(name = "due_date") + private LocalDateTime dueDate; //установленное создателем время окончания + + @Column(name = "expired") + private Boolean expired; + + @Column(name = "web_url") + private String webUrl; +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java new file mode 100644 index 0000000..d6cbbb9 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java @@ -0,0 +1,21 @@ +package dev.struchkov.bot.gitlab.context.domain.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Embeddable; + +/** + * Сущность References. + * + * @author Dmitry Sheyko [17.01.2023] + */ + +@Embeddable +@Getter +@Setter +public class References { + private String shortReference; + private String relativeReference; + private String fullReference; +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java new file mode 100644 index 0000000..5361f09 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java @@ -0,0 +1,20 @@ +package dev.struchkov.bot.gitlab.context.domain.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Embeddable; + +/** + * Сущность TaskCompletionStatus. + * + * @author Dmitry Sheyko [17.01.2023] + */ + +@Embeddable +@Getter +@Setter +public class TaskCompletionStatus { + private Integer count; + private Integer completedCount; +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java new file mode 100644 index 0000000..672feb5 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java @@ -0,0 +1,23 @@ +package dev.struchkov.bot.gitlab.context.domain.entity; + +import lombok.Getter; +import lombok.Setter; + +import javax.persistence.Embeddable; + +/** + * Сущность TimeStats. + * + * @author Dmitry Sheyko [17.01.2023] + */ + +@Embeddable +@Getter +@Setter +public class TimeStats { + private Integer timeEstimate; + private Integer totalTimeSpent; // количество секунд затраченых на работы, пример 37800" + private String humanTimeEstimate; + private String humanTotalTimeSpent; // Время строкой, пример "10h 30m" +} + diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java new file mode 100644 index 0000000..e16dfec --- /dev/null +++ b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java @@ -0,0 +1,10 @@ +package dev.struchkov.bot.gitlab.core.service.parser; + +import dev.struchkov.bot.gitlab.context.domain.IssueState; + +import java.util.Set; + +public class IssueRequestParser { +private static final Set OLD_STATUSES = Set.of( + IssueState.OPENED, IssueState.CLOSED); +} diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java new file mode 100644 index 0000000..88449b4 --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java @@ -0,0 +1,108 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +@Data +public class IssueJson { + private Long id; + + @JsonProperty("iid") + private Long twoId; + + @JsonProperty("project_id") + private Long projectId; + private String title; + private String description; + private IssueStateJson state; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("created_at") + private LocalDateTime createdDate; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("updated_at") + private LocalDateTime updatedDate; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("closed_at") + private LocalDateTime closedDate; + + @JsonProperty("closed_by") + private PersonJson closedBy; + + private Set labels; + private MilestoneJson milestone; + private Set assignees; + private PersonJson author; + private IssueTypeJson type; + private PersonJson assignee; + + @JsonProperty("user_notes_count") + private Integer userNotesCount; + + @JsonProperty("merge_requests_count") + private Integer mergeRequestsCount; + + @JsonProperty("upvotes") + private Integer upVotes; + + @JsonProperty("downvotes") + private Integer downVotes; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("due_date") + private LocalDateTime dueDate; + private Boolean confidential; + + @JsonProperty("discussion_locked") + private Integer discussionLocked; //TODO выяснить тип поля + + @JsonProperty("issue_type") + private String issueType; //TODO выяснить зачем дублирует поле type + + @JsonProperty("web_url") + private String webUrl; + + @JsonProperty("time_stats") + private TimeStatsJson timeStats; + + @JsonProperty("task_completion_status") + private TaskCompletionStatusJson taskCompletionStatus; + + @JsonProperty("blocking_issues_count") + private Integer blockingIssuesCount; + + @JsonProperty("has_tasks") + private Boolean hasTasks; + + @JsonProperty("_links") + private LinksJson links; + + private ReferencesJson references; + private String severity; //TODO заменить на енум "UNKNOWN", Critical - S1, High - S2, Medium - S3, Low - S4, + + @JsonProperty("moved_to_id") + private Long movedToId; + + @JsonProperty("service_desk_reply_to") + private Long serviceDescReplyTo; //TODO не понятен тип поля + + @JsonProperty("epic_issue_id") + private Long epicId; // "epic_issue_id" Поле доступное только для премиум акаунтов +} \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java new file mode 100644 index 0000000..a62462d --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java @@ -0,0 +1,15 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +public enum IssueStateJson { + @JsonProperty("opened") + OPENED, + + @JsonProperty("closed") + CLOSED +} \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java new file mode 100644 index 0000000..df25883 --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java @@ -0,0 +1,6 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +public enum IssueTypeJson { + ISSUE, + INCIDENT +} diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/LinksJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/LinksJson.java new file mode 100644 index 0000000..af13add --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/LinksJson.java @@ -0,0 +1,21 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +@Data +public class LinksJson { + private String self; + private String notes; + + @JsonProperty("award_emoji") + private String awardEmoji; + private String project; + + @JsonProperty("closed_as_duplicate_of") + private String closedAsDuplicateOf; +} \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java new file mode 100644 index 0000000..cd9adcb --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java @@ -0,0 +1,52 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +@Data +public class MilestoneJson { + private Long id; + + @JsonProperty("iid") + private Long twoId; + + @JsonProperty("project_id") + private Long projectId; + private String title; + private String description; + private MilestoneStateJson state; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("created_at") + private LocalDateTime createdDate; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("updated_at") + private LocalDateTime updatedDate; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("start_date") + private LocalDateTime startDate; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonProperty("due_date") + private LocalDateTime dueDate; + private boolean expired; + + @JsonProperty("web_url") + private String webUrl; +} \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java new file mode 100644 index 0000000..3666d1b --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java @@ -0,0 +1,15 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +public enum MilestoneStateJson { + @JsonProperty("active") + ACTIVE, + + @JsonProperty("closed") + CLOSED +} \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java new file mode 100644 index 0000000..4f1e2d0 --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java @@ -0,0 +1,20 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +@Data +public class ReferencesJson { + @JsonProperty("short") + private String shortReference; + + @JsonProperty("relative") + private String relativeReference; + + @JsonProperty("full") + private String fullReference; +} diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TaskCompletionStatusJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TaskCompletionStatusJson.java new file mode 100644 index 0000000..6da0be3 --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TaskCompletionStatusJson.java @@ -0,0 +1,17 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +@Data +public class TaskCompletionStatusJson { + @JsonProperty("count") + private Integer count; + + @JsonProperty("completed_count") + private Integer completedCount; +} \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java new file mode 100644 index 0000000..91d3943 --- /dev/null +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java @@ -0,0 +1,15 @@ +package dev.struchkov.bot.gitlab.sdk.domain; + +import lombok.Data; + +/** + * @author Dmitry Sheyko [17.01.2023] + */ + +@Data +public class TimeStatsJson { + private Integer timeEstimate; + private Integer totalTimeSpent; // количество секунд затраченых на работы, пример 37800" + private String humanTimeEstimate; + private String humanTotalTimeSpent; // Время строкой, пример "10h 30m" +} \ No newline at end of file From b0fb70e1509dca90acc0ffb75bc4bdc43145ec0d Mon Sep 17 00:00:00 2001 From: DmitrySheyko Date: Thu, 19 Jan 2023 18:39:06 +0300 Subject: [PATCH 2/5] =?UTF-8?q?Feat:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=20changelog=20=D0=B4=D0=BB=D1=8F=20=D1=82=D0=B0=D0=B1?= =?UTF-8?q?=D0=BB=D0=B8=D1=86=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20issue.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitlab/context/domain/entity/Issue.java | 38 ++-- .../context/domain/entity/Milestone.java | 2 +- .../2023-01-19-create-tables-for-issue.xml | 206 ++++++++++++++++++ .../resources/liquibase/v.2.0.0/changelog.xml | 1 + .../bot/gitlab/sdk/domain/IssueJson.java | 6 +- .../bot/gitlab/sdk/domain/TimeStatsJson.java | 9 + 6 files changed, 239 insertions(+), 23 deletions(-) create mode 100644 gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java index 88aad0c..0253047 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java @@ -34,7 +34,7 @@ public class Issue { @EqualsAndHashCode.Include private Long id; - @Column(name = "iid") + @Column(name = "two_id") private Long twoId; @Column(name = "project_id") @@ -64,7 +64,7 @@ public class Issue { private Person closedBy; @ElementCollection - @CollectionTable(name = "issue_label", joinColumns = @JoinColumn(name = "label_id")) + @CollectionTable(name = "issue_labels", joinColumns = @JoinColumn(name = "label_id")) @Column(name = "labels") private Set labels = new HashSet<>(); @@ -93,29 +93,29 @@ public class Issue { private IssueType type; // ОБразец приходящего значения "INCIDENT" @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) - @JoinColumn(name = "assignee") + @JoinColumn(name = "assignee_id") private Person assignee; - @Column(name = "user_notes_count") //Количество комментов пользователя + @Column(name = "user_notes_count") private Integer userNotesCount; @Column(name = "merge_requests_count") private Integer mergeRequestsCount; - @Column(name = "upvotes") // Количество лайков + @Column(name = "up_votes") private Integer upVotes; - @Column(name = "downvotes") // Количество дизлайков + @Column(name = "down_votes") private Integer downVotes; @Column(name = "due_date") private LocalDateTime dueDate; - @Column(name = "confidential") // Конфиденцальное или нет + @Column(name = "confidential") private Boolean confidential; @Column(name = "discussion_locked") - private Integer discussionLocked; //TODO выяснить тип поляя. + private Integer discussionLocked; @Column(name = "issue_type") private String issueType; //TODO выяснить зачем дублирует поле type Образец приходящего значения "incident" @@ -123,7 +123,6 @@ public class Issue { @Column(name = "web_url") private String webUrl; - @Embedded @AttributeOverrides({ @AttributeOverride(name = "timeEstimate", column = @Column(name = "time_estimate")), @@ -135,8 +134,8 @@ public class Issue { @Embedded @AttributeOverrides({ - @AttributeOverride(name = "count", column = @Column(name = "count")), - @AttributeOverride(name = "completedCount", column = @Column(name = "completed_count")) + @AttributeOverride(name = "count", column = @Column(name = "task_count")), + @AttributeOverride(name = "completedCount", column = @Column(name = "task_completed_count")) }) private TaskCompletionStatus taskCompletionStatus; @@ -148,11 +147,11 @@ public class Issue { @Embedded @AttributeOverrides({ - @AttributeOverride(name = "self", column = @Column(name = "self")), - @AttributeOverride(name = "notes", column = @Column(name = "notes")), - @AttributeOverride(name = "awardEmoji", column = @Column(name = "award_emoji")), - @AttributeOverride(name = "project", column = @Column(name = "project")), - @AttributeOverride(name = "closedAsDuplicateOf", column = @Column(name = "closed_as_duplicate_of")) + @AttributeOverride(name = "self", column = @Column(name = "link_to_self")), + @AttributeOverride(name = "notes", column = @Column(name = "link_to_notes")), + @AttributeOverride(name = "awardEmoji", column = @Column(name = "link_to_award_emoji")), + @AttributeOverride(name = "project", column = @Column(name = "link_to_project")), + @AttributeOverride(name = "closedAsDuplicateOf", column = @Column(name = "link_to_closed_as_duplicate_of")) }) private Links links; @@ -164,8 +163,9 @@ public class Issue { }) private References references; - /*Возможно надо заменить на енум "UNKNOWN", Critical - S1, High - S2, Medium - S3, Low - S4. - Но это поле доступно только для премиум акаунтов + /** + Возможно надо заменить на енум: "UNKNOWN", "Critical - S1", "High - S2", "Medium - S3", "Low - S4". + Но выбор любых значений кроме "UNKNOWN" доступен только для премиум акаунтов и я не могу получить точные значения котоые оно принимает. */ @Column(name = "severity") private String severity; @@ -174,7 +174,7 @@ public class Issue { private Long movedToId; @Column(name = "service_desk_reply_to") - private String serviceDescReplyTo; //TODO не понятен тип поля + private String serviceDescReplyTo; @Column(name = "epic_issue_id") private Long epicId; // "epic_issue_id" Поле доснтупное только для премиум акаунтов diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java index 3bde4d5..ed7e7b3 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java @@ -28,7 +28,7 @@ public class Milestone { @EqualsAndHashCode.Include private Long id; - @Column(name = "iid") + @Column(name = "two_id") private Long twoId; @Column(name = "project_id") diff --git a/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml b/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml new file mode 100644 index 0000000..5d11b50 --- /dev/null +++ b/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gitlab-app/src/main/resources/liquibase/v.2.0.0/changelog.xml b/gitlab-app/src/main/resources/liquibase/v.2.0.0/changelog.xml index 31d0925..a392bdb 100644 --- a/gitlab-app/src/main/resources/liquibase/v.2.0.0/changelog.xml +++ b/gitlab-app/src/main/resources/liquibase/v.2.0.0/changelog.xml @@ -9,5 +9,6 @@ + \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java index 88449b4..11acb8e 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java @@ -71,7 +71,7 @@ public class IssueJson { private Boolean confidential; @JsonProperty("discussion_locked") - private Integer discussionLocked; //TODO выяснить тип поля + private Integer discussionLocked; @JsonProperty("issue_type") private String issueType; //TODO выяснить зачем дублирует поле type @@ -95,13 +95,13 @@ public class IssueJson { private LinksJson links; private ReferencesJson references; - private String severity; //TODO заменить на енум "UNKNOWN", Critical - S1, High - S2, Medium - S3, Low - S4, + private String severity; @JsonProperty("moved_to_id") private Long movedToId; @JsonProperty("service_desk_reply_to") - private Long serviceDescReplyTo; //TODO не понятен тип поля + private Long serviceDescReplyTo; @JsonProperty("epic_issue_id") private Long epicId; // "epic_issue_id" Поле доступное только для премиум акаунтов diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java index 91d3943..4eeef90 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/TimeStatsJson.java @@ -1,5 +1,6 @@ package dev.struchkov.bot.gitlab.sdk.domain; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; /** @@ -8,8 +9,16 @@ import lombok.Data; @Data public class TimeStatsJson { + + @JsonProperty("time_estimate") private Integer timeEstimate; + + @JsonProperty("total_time_spent") private Integer totalTimeSpent; // количество секунд затраченых на работы, пример 37800" + + @JsonProperty("human_time_estimate") private String humanTimeEstimate; + + @JsonProperty("human_total_time_spent") private String humanTotalTimeSpent; // Время строкой, пример "10h 30m" } \ No newline at end of file From 331ddd731431ec1c47f7ef6746cb273905c3b7d6 Mon Sep 17 00:00:00 2001 From: Struchkov Mark Date: Fri, 20 Jan 2023 18:36:39 +0300 Subject: [PATCH 3/5] =?UTF-8?q?=D0=A3=D0=BF=D1=80=D0=BE=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20Iss?= =?UTF-8?q?ue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitlab/context/domain/entity/Issue.java | 85 +++++-------------- .../gitlab/context/domain/entity/Links.java | 23 ----- .../context/domain/entity/Milestone.java | 64 -------------- .../context/domain/entity/References.java | 21 ----- .../domain/entity/TaskCompletionStatus.java | 20 ----- .../context/domain/entity/TimeStats.java | 23 ----- 6 files changed, 23 insertions(+), 213 deletions(-) delete mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java delete mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java delete mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java delete mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java delete mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java index 0253047..901a566 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java @@ -8,9 +8,24 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; -import javax.persistence.*; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; @@ -68,10 +83,6 @@ public class Issue { @Column(name = "labels") private Set labels = new HashSet<>(); - @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) - @JoinColumn(name = "milestone_id") - private Milestone milestone; - @Column(name = "assignees") @OneToMany( fetch = FetchType.LAZY, @@ -82,7 +93,7 @@ public class Issue { joinColumns = @JoinColumn(name = "issue_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "person_id", referencedColumnName = "id") ) - private Set assignees = new HashSet<>(); + private List assignees = new ArrayList<>(); @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn(name = "author_id") @@ -90,11 +101,7 @@ public class Issue { @Enumerated(value = EnumType.STRING) @Column(name = "type") - private IssueType type; // ОБразец приходящего значения "INCIDENT" - - @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) - @JoinColumn(name = "assignee_id") - private Person assignee; + private IssueType type; @Column(name = "user_notes_count") private Integer userNotesCount; @@ -117,65 +124,19 @@ public class Issue { @Column(name = "discussion_locked") private Integer discussionLocked; - @Column(name = "issue_type") - private String issueType; //TODO выяснить зачем дублирует поле type Образец приходящего значения "incident" + @Column(name = "task_count") + private Integer taskCount; + + @Column(name = "task_completed_count") + private Integer taskCompletedCount; @Column(name = "web_url") private String webUrl; - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "timeEstimate", column = @Column(name = "time_estimate")), - @AttributeOverride(name = "totalTimeSpent", column = @Column(name = "total_time_spent")), - @AttributeOverride(name = "humanTimeEstimate", column = @Column(name = "human_time_estimate")), - @AttributeOverride(name = "humanTotalTimeSpent", column = @Column(name = "human_total_time_spent")) - }) - private TimeStats timeStats; - - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "count", column = @Column(name = "task_count")), - @AttributeOverride(name = "completedCount", column = @Column(name = "task_completed_count")) - }) - private TaskCompletionStatus taskCompletionStatus; - @Column(name = "blocking_issues_count") private Integer blockingIssuesCount; @Column(name = "has_tasks") private Boolean hasTasks; - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "self", column = @Column(name = "link_to_self")), - @AttributeOverride(name = "notes", column = @Column(name = "link_to_notes")), - @AttributeOverride(name = "awardEmoji", column = @Column(name = "link_to_award_emoji")), - @AttributeOverride(name = "project", column = @Column(name = "link_to_project")), - @AttributeOverride(name = "closedAsDuplicateOf", column = @Column(name = "link_to_closed_as_duplicate_of")) - }) - private Links links; - - @Embedded - @AttributeOverrides({ - @AttributeOverride(name = "shortReference", column = @Column(name = "short_reference")), - @AttributeOverride(name = "relativeReference", column = @Column(name = "relative_reference")), - @AttributeOverride(name = "fullReference", column = @Column(name = "full_reference")) - }) - private References references; - - /** - Возможно надо заменить на енум: "UNKNOWN", "Critical - S1", "High - S2", "Medium - S3", "Low - S4". - Но выбор любых значений кроме "UNKNOWN" доступен только для премиум акаунтов и я не могу получить точные значения котоые оно принимает. - */ - @Column(name = "severity") - private String severity; - - @Column(name = "moved_to_id") - private Long movedToId; - - @Column(name = "service_desk_reply_to") - private String serviceDescReplyTo; - - @Column(name = "epic_issue_id") - private Long epicId; // "epic_issue_id" Поле доснтупное только для премиум акаунтов } \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java deleted file mode 100644 index 0537369..0000000 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Links.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.struchkov.bot.gitlab.context.domain.entity; - -import lombok.Getter; -import lombok.Setter; - -import javax.persistence.Embeddable; - -/** - * Сущность Issue. - * - * @author Dmitry Sheyko [17.01.2023] - */ - -@Embeddable -@Getter -@Setter -public class Links { - private String self; - private String notes; - private String awardEmoji; - private String project; - private String closedAsDuplicateOf; -} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java deleted file mode 100644 index ed7e7b3..0000000 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Milestone.java +++ /dev/null @@ -1,64 +0,0 @@ -package dev.struchkov.bot.gitlab.context.domain.entity; - -import dev.struchkov.bot.gitlab.context.domain.MilestoneState; -import dev.struchkov.haiti.utils.fieldconstants.annotation.FieldNames; -import dev.struchkov.haiti.utils.fieldconstants.domain.Mode; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; - -import javax.persistence.*; -import java.time.LocalDateTime; - -/** - * Сущность Milestone. - * - * @author Dmitry Sheyko 17.01.2023 - */ - -@Entity -@Getter -@Setter -@EqualsAndHashCode(onlyExplicitlyIncluded = true) -@Table(name = "milestone") -@FieldNames(mode = {Mode.TABLE, Mode.SIMPLE}) -public class Milestone { - - @Id - @EqualsAndHashCode.Include - private Long id; - - @Column(name = "two_id") - private Long twoId; - - @Column(name = "project_id") - private Long projectId; - - @Column(name = "title") - private String title; - - @Column(name = "description") - private String description; - - @Enumerated(value = EnumType.STRING) - @Column(name = "state") - private MilestoneState state; - - @Column(name = "created_date") - private LocalDateTime createdDate; - - @Column(name = "updated_date") - private LocalDateTime updatedDate; - - @Column(name = "start_date") - private LocalDateTime startDate; //установленное создателем время начала - - @Column(name = "due_date") - private LocalDateTime dueDate; //установленное создателем время окончания - - @Column(name = "expired") - private Boolean expired; - - @Column(name = "web_url") - private String webUrl; -} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java deleted file mode 100644 index d6cbbb9..0000000 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/References.java +++ /dev/null @@ -1,21 +0,0 @@ -package dev.struchkov.bot.gitlab.context.domain.entity; - -import lombok.Getter; -import lombok.Setter; - -import javax.persistence.Embeddable; - -/** - * Сущность References. - * - * @author Dmitry Sheyko [17.01.2023] - */ - -@Embeddable -@Getter -@Setter -public class References { - private String shortReference; - private String relativeReference; - private String fullReference; -} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java deleted file mode 100644 index 5361f09..0000000 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TaskCompletionStatus.java +++ /dev/null @@ -1,20 +0,0 @@ -package dev.struchkov.bot.gitlab.context.domain.entity; - -import lombok.Getter; -import lombok.Setter; - -import javax.persistence.Embeddable; - -/** - * Сущность TaskCompletionStatus. - * - * @author Dmitry Sheyko [17.01.2023] - */ - -@Embeddable -@Getter -@Setter -public class TaskCompletionStatus { - private Integer count; - private Integer completedCount; -} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java deleted file mode 100644 index 672feb5..0000000 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/TimeStats.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.struchkov.bot.gitlab.context.domain.entity; - -import lombok.Getter; -import lombok.Setter; - -import javax.persistence.Embeddable; - -/** - * Сущность TimeStats. - * - * @author Dmitry Sheyko [17.01.2023] - */ - -@Embeddable -@Getter -@Setter -public class TimeStats { - private Integer timeEstimate; - private Integer totalTimeSpent; // количество секунд затраченых на работы, пример 37800" - private String humanTimeEstimate; - private String humanTotalTimeSpent; // Время строкой, пример "10h 30m" -} - From 30ff49eb75d2086ba2ebdb86cbb79fe5ac8c54c3 Mon Sep 17 00:00:00 2001 From: DmitrySheyko Date: Sun, 22 Jan 2023 00:56:50 +0300 Subject: [PATCH 4/5] =?UTF-8?q?Fix:=20=D0=B2=D0=BD=D0=B5=D1=81=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B8=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C=D1=82=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=D0=BC=20=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=201.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bot/gitlab/context/domain/IssueState.java | 5 + .../bot/gitlab/context/domain/IssueType.java | 8 +- .../gitlab/context/domain/MilestoneState.java | 5 - .../gitlab/context/domain/entity/Issue.java | 3 - .../service/parser/IssueRequestParser.java | 10 -- .../2023-01-19-create-tables-for-issue.xml | 116 ++---------------- .../bot/gitlab/sdk/domain/IssueJson.java | 7 +- .../bot/gitlab/sdk/domain/IssueStateJson.java | 3 +- .../bot/gitlab/sdk/domain/IssueTypeJson.java | 8 +- .../bot/gitlab/sdk/domain/MilestoneJson.java | 3 +- .../gitlab/sdk/domain/MilestoneStateJson.java | 3 +- .../bot/gitlab/sdk/domain/ReferencesJson.java | 3 +- 12 files changed, 38 insertions(+), 136 deletions(-) delete mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java delete mode 100644 bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java index 2b86c09..498cd36 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueState.java @@ -1,5 +1,10 @@ package dev.struchkov.bot.gitlab.context.domain; +/** + * @author Dmitry Sheyko 21.01.2021 + */ public enum IssueState { + OPENED, CLOSED + } \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java index 62bb1e5..4a4e477 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IssueType.java @@ -1,6 +1,10 @@ package dev.struchkov.bot.gitlab.context.domain; +/** + * @author Dmitry Sheyko 21.01.2021 + */ public enum IssueType { - ISSUE, - INCIDENT + + ISSUE, INCIDENT + } diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java deleted file mode 100644 index 4c0fb32..0000000 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/MilestoneState.java +++ /dev/null @@ -1,5 +0,0 @@ -package dev.struchkov.bot.gitlab.context.domain; - -public enum MilestoneState { - ACTIVE, CLOSED -} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java index 901a566..156f888 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java @@ -28,14 +28,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; - /** * Сущность Issue. * * @author Dmitry Sheyko [17.01.2023] */ - -// При запросе issue учесть что пагинация по умолчанию - 20 объектов @Getter @Setter @Entity diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java deleted file mode 100644 index e16dfec..0000000 --- a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueRequestParser.java +++ /dev/null @@ -1,10 +0,0 @@ -package dev.struchkov.bot.gitlab.core.service.parser; - -import dev.struchkov.bot.gitlab.context.domain.IssueState; - -import java.util.Set; - -public class IssueRequestParser { -private static final Set OLD_STATUSES = Set.of( - IssueState.OPENED, IssueState.CLOSED); -} diff --git a/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml b/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml index 5d11b50..7c2c0d8 100644 --- a/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml +++ b/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml @@ -3,48 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.17.xsd"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -76,18 +35,12 @@ - - - - - - @@ -109,88 +62,37 @@ - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + - + diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java index 11acb8e..6d7863d 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java @@ -13,9 +13,9 @@ import java.util.Set; /** * @author Dmitry Sheyko [17.01.2023] */ - @Data public class IssueJson { + private Long id; @JsonProperty("iid") @@ -74,7 +74,7 @@ public class IssueJson { private Integer discussionLocked; @JsonProperty("issue_type") - private String issueType; //TODO выяснить зачем дублирует поле type + private String issueType; @JsonProperty("web_url") private String webUrl; @@ -104,5 +104,6 @@ public class IssueJson { private Long serviceDescReplyTo; @JsonProperty("epic_issue_id") - private Long epicId; // "epic_issue_id" Поле доступное только для премиум акаунтов + private Long epicId; + } \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java index a62462d..b37d121 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueStateJson.java @@ -5,11 +5,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** * @author Dmitry Sheyko [17.01.2023] */ - public enum IssueStateJson { + @JsonProperty("opened") OPENED, @JsonProperty("closed") CLOSED + } \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java index df25883..8eba66c 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueTypeJson.java @@ -1,6 +1,10 @@ package dev.struchkov.bot.gitlab.sdk.domain; +/** + * @author Dmitry Sheyko 21.01.2021 + */ public enum IssueTypeJson { - ISSUE, - INCIDENT + + ISSUE, INCIDENT + } diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java index cd9adcb..33200ec 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java @@ -12,9 +12,9 @@ import java.time.LocalDateTime; /** * @author Dmitry Sheyko [17.01.2023] */ - @Data public class MilestoneJson { + private Long id; @JsonProperty("iid") @@ -49,4 +49,5 @@ public class MilestoneJson { @JsonProperty("web_url") private String webUrl; + } \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java index 3666d1b..fca77ad 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneStateJson.java @@ -5,11 +5,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** * @author Dmitry Sheyko [17.01.2023] */ - public enum MilestoneStateJson { + @JsonProperty("active") ACTIVE, @JsonProperty("closed") CLOSED + } \ No newline at end of file diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java index 4f1e2d0..134422f 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/ReferencesJson.java @@ -6,9 +6,9 @@ import lombok.Data; /** * @author Dmitry Sheyko [17.01.2023] */ - @Data public class ReferencesJson { + @JsonProperty("short") private String shortReference; @@ -17,4 +17,5 @@ public class ReferencesJson { @JsonProperty("full") private String fullReference; + } From 7e1c6c1f4485b743a4f2e4ea27b7d5237135d403 Mon Sep 17 00:00:00 2001 From: DmitrySheyko Date: Fri, 3 Feb 2023 20:15:46 +0300 Subject: [PATCH 5/5] =?UTF-8?q?Feat:=20=D0=B7=D0=B0=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=88=D0=B5=D0=BD=20=D0=BE=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=20issue,=20=D0=B2=D1=8B=D0=B3=D1=80=D1=83=D0=B7=D0=BA?= =?UTF-8?q?=D0=B0,=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5,=20?= =?UTF-8?q?=D1=83=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../context/domain/AssigneesChanged.java | 50 +++ .../context/domain/IdAndStatusIssue.java | 20 ++ .../gitlab/context/domain/entity/Issue.java | 14 +- .../issue/DeleteFromAssigneesNotify.java | 33 ++ .../notify/issue/DescriptionIssueNotify.java | 33 ++ .../notify/issue/DueDateIssueNotify.java | 36 ++ .../domain/notify/issue/IssueNotify.java | 29 ++ .../domain/notify/issue/NewIssueNotify.java | 47 +++ .../notify/issue/StatusIssueNotify.java | 37 +++ .../domain/notify/issue/TitleIssueNotify.java | 33 ++ .../domain/notify/issue/TypeIssueNotify.java | 37 +++ .../context/repository/IssueRepository.java | 27 ++ .../gitlab/context/service/IssueService.java | 29 ++ .../bot/gitlab/context/utils/Icons.java | 3 +- .../config/properties/GitlabProperty.java | 7 + .../service/convert/IssueJsonConverter.java | 97 ++++++ .../core/service/impl/IssueServiceImpl.java | 313 ++++++++++++++++++ .../core/service/parser/IssueParser.java | 166 ++++++++++ .../forktask/GetAllIssueForProjectTask.java | 55 +++ .../parser/forktask/GetSingleIssueTask.java | 36 ++ .../gitlab/data/impl/IssueRepositoryImpl.java | 56 ++++ .../gitlab/data/jpa/IssueJpaRepository.java | 22 ++ .../gitlab/scheduler/SchedulerService.java | 8 + gitlab-app/src/main/resources/application.yml | 6 +- .../2023-01-19-create-tables-for-issue.xml | 6 + .../bot/gitlab/sdk/domain/IssueJson.java | 12 +- .../bot/gitlab/sdk/domain/MilestoneJson.java | 15 +- ...teFromAssigneesOfIssueNotifyGenerator.java | 37 +++ .../DescriptionIssueNotifyGenerator.java | 39 +++ .../notify/DueDateIssueNotifyGenerator.java | 38 +++ .../notify/NewIssueNotifyGenerator.java | 49 +++ .../notify/StatusIssueNotifyGenerator.java | 38 +++ .../notify/TitleIssueNotifyGenerator.java | 39 +++ .../notify/TypeIssueNotifyGenerator.java | 38 +++ 34 files changed, 1489 insertions(+), 16 deletions(-) create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/AssigneesChanged.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IdAndStatusIssue.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DeleteFromAssigneesNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DescriptionIssueNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DueDateIssueNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/IssueNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/NewIssueNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/StatusIssueNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TitleIssueNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TypeIssueNotify.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/repository/IssueRepository.java create mode 100644 bot-context/src/main/java/dev/struchkov/bot/gitlab/context/service/IssueService.java create mode 100644 bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/convert/IssueJsonConverter.java create mode 100644 bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/impl/IssueServiceImpl.java create mode 100644 bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueParser.java create mode 100644 bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetAllIssueForProjectTask.java create mode 100644 bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetSingleIssueTask.java create mode 100644 bot-data/src/main/java/dev/struchkov/bot/gitlab/data/impl/IssueRepositoryImpl.java create mode 100644 bot-data/src/main/java/dev/struchkov/bot/gitlab/data/jpa/IssueJpaRepository.java create mode 100644 telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DeleteFromAssigneesOfIssueNotifyGenerator.java create mode 100644 telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DescriptionIssueNotifyGenerator.java create mode 100644 telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DueDateIssueNotifyGenerator.java create mode 100644 telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/NewIssueNotifyGenerator.java create mode 100644 telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/StatusIssueNotifyGenerator.java create mode 100644 telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TitleIssueNotifyGenerator.java create mode 100644 telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TypeIssueNotifyGenerator.java diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/AssigneesChanged.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/AssigneesChanged.java new file mode 100644 index 0000000..4e55368 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/AssigneesChanged.java @@ -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 oldAssignees, List newAssignees) { + final Map oldMap = oldAssignees.stream().collect(Collectors.toMap(Person::getId, p -> p)); + final Map 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; + }; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IdAndStatusIssue.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IdAndStatusIssue.java new file mode 100644 index 0000000..f257603 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/IdAndStatusIssue.java @@ -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; + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java index 156f888..e3d57d2 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/entity/Issue.java @@ -22,6 +22,7 @@ import javax.persistence.JoinTable; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; +import java.time.LocalDate; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.HashSet; @@ -76,11 +77,10 @@ public class Issue { private Person closedBy; @ElementCollection - @CollectionTable(name = "issue_labels", joinColumns = @JoinColumn(name = "label_id")) - @Column(name = "labels") + @CollectionTable(name = "issue_label", joinColumns = @JoinColumn(name = "issue_id")) + @Column(name = "label") private Set labels = new HashSet<>(); - @Column(name = "assignees") @OneToMany( fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE} @@ -113,7 +113,7 @@ public class Issue { private Integer downVotes; @Column(name = "due_date") - private LocalDateTime dueDate; + private LocalDate dueDate; @Column(name = "confidential") private Boolean confidential; @@ -136,4 +136,10 @@ public class Issue { @Column(name = "has_tasks") private Boolean hasTasks; + @Column(name = "notification") + private boolean notification; + + @Column(name = "is_assignee") + private boolean userAssignee; + } \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DeleteFromAssigneesNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DeleteFromAssigneesNotify.java new file mode 100644 index 0000000..3e64acb --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DeleteFromAssigneesNotify.java @@ -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; + } + +} diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DescriptionIssueNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DescriptionIssueNotify.java new file mode 100644 index 0000000..d61e54d --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DescriptionIssueNotify.java @@ -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; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DueDateIssueNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DueDateIssueNotify.java new file mode 100644 index 0000000..b00f295 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/DueDateIssueNotify.java @@ -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; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/IssueNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/IssueNotify.java new file mode 100644 index 0000000..9a109d6 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/IssueNotify.java @@ -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; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/NewIssueNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/NewIssueNotify.java new file mode 100644 index 0000000..48a66f3 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/NewIssueNotify.java @@ -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 labels; + private final String confidential; + + @Builder + public NewIssueNotify( + String projectName, + String title, + String url, + String issueType, + String author, + String description, + String dueDate, + Set 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; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/StatusIssueNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/StatusIssueNotify.java new file mode 100644 index 0000000..6377a63 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/StatusIssueNotify.java @@ -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; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TitleIssueNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TitleIssueNotify.java new file mode 100644 index 0000000..ec46de7 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TitleIssueNotify.java @@ -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; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TypeIssueNotify.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TypeIssueNotify.java new file mode 100644 index 0000000..3c68833 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/domain/notify/issue/TypeIssueNotify.java @@ -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; + } + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/repository/IssueRepository.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/repository/IssueRepository.java new file mode 100644 index 0000000..62780d4 --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/repository/IssueRepository.java @@ -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 findAllIdByStateIn(@NonNull Set states); + + Issue save(Issue issue); + + Optional findById(Long issueId); + + List findAllById(Set mergeRequestIds); + + void deleteByStates(Set states); + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/service/IssueService.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/service/IssueService.java new file mode 100644 index 0000000..e40e09a --- /dev/null +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/service/IssueService.java @@ -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 updateAll(@NonNull List issues); + + ExistContainer existsById(@NonNull Set issueIds); + + List createAll(List issues); + + Set getAllId(Set statuses); + + void cleanOld(); + +} \ No newline at end of file diff --git a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/utils/Icons.java b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/utils/Icons.java index 49de493..8244fcd 100644 --- a/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/utils/Icons.java +++ b/bot-context/src/main/java/dev/struchkov/bot/gitlab/context/utils/Icons.java @@ -19,6 +19,7 @@ public class Icons { public static final String PEN = "✏️"; public static final String BUILD = "\uD83D\uDEE0"; public static final String LINK = "\uD83D\uDD17"; + public static final String BELL ="\uD83D\uDD14"; private Icons() { utilityClass(); @@ -28,4 +29,4 @@ public class Icons { return "[" + escapeMarkdown(title) + "](" + url + ")"; } -} +} \ No newline at end of file diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/config/properties/GitlabProperty.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/config/properties/GitlabProperty.java index 2fb2a8e..e3ca4a1 100644 --- a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/config/properties/GitlabProperty.java +++ b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/config/properties/GitlabProperty.java @@ -68,4 +68,11 @@ public class GitlabProperty { private String discussionUrl; + /** + * Адрес, по которому можно получить ISSUE + */ + private String issueUrl; + + private String openIssueUrl; + } diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/convert/IssueJsonConverter.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/convert/IssueJsonConverter.java new file mode 100644 index 0000000..54d9315 --- /dev/null +++ b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/convert/IssueJsonConverter.java @@ -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 { + + 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 jsonAssignees) { + if (checkNotEmpty(jsonAssignees)) { + final List assignees = jsonAssignees.stream() + .map(convertPerson::convert) + .toList(); + issue.setAssignees(assignees); + } + } + + private void convertLabels(Issue issue, Set source) { + if (checkNotEmpty(source)) { + final Set 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; + }; + } + +} \ No newline at end of file diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/impl/IssueServiceImpl.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/impl/IssueServiceImpl.java new file mode 100644 index 0000000..bfa9e64 --- /dev/null +++ b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/impl/IssueServiceImpl.java @@ -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 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 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 existsById(@NonNull Set issueIds) { + final List existsEntity = repository.findAllById(issueIds); + final Set existsIds = existsEntity.stream().map(Issue::getId).collect(Collectors.toSet()); + if (existsIds.containsAll(issueIds)) { + return ExistContainer.allFind(existsEntity); + } else { + final Set noExistsId = issueIds.stream() + .filter(id -> !existsIds.contains(id)) + .collect(Collectors.toSet()); + return ExistContainer.notAllFind(existsEntity, noExistsId); + } + } + + @Override + public List createAll(List newIssues) { + return newIssues.stream() + .map(this::create) + .toList(); + } + + @Override + @Transactional + public List updateAll(@NonNull List issues) { + return issues.stream() + .map(this::update) + .collect(Collectors.toList()); + } + + @Override + public Set getAllId(Set 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"); + } + +} \ No newline at end of file diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueParser.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueParser.java new file mode 100644 index 0000000..18ef891 --- /dev/null +++ b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/IssueParser.java @@ -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 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 existIds = issueService.getAllId(OLD_STATUSES); + + final List 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 getOldIssues(Set existIds) { + final List>> 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 projectIds = projectService.getAllIds(); + + /** + * На основе id проекта, url для получения issues по id проекта и токена пользователя + * выгружаем из GitLab список всех IssueJson. Получаем в многопоточном режиме. + */ + final List issueJsons = getIssues(projectIds); + + /** + * Получаем id всех IssueJson загруженных из GitLab + */ + if (checkNotEmpty(issueJsons)) { + final Set jsonIds = issueJsons.stream() + .map(IssueJson::getId) + .collect(Collectors.toSet()); + + final ExistContainer existContainer = issueService.existsById(jsonIds); + log.trace("Из {} полученных MR не найдены в хранилище {}", jsonIds.size(), existContainer.getIdNoFound().size()); + if (!existContainer.isAllFound()) { + final List 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 getIssues(Set projectIds) { + final List>> 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 newIssues) { + final Map 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()) + ); + } + } + +} \ No newline at end of file diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetAllIssueForProjectTask.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetAllIssueForProjectTask.java new file mode 100644 index 0000000..e217843 --- /dev/null +++ b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetAllIssueForProjectTask.java @@ -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> { + + 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 compute() { + Thread.sleep(200); + final List 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 getIssueJsons() { + final List 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; + } + +} \ No newline at end of file diff --git a/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetSingleIssueTask.java b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetSingleIssueTask.java new file mode 100644 index 0000000..f237c67 --- /dev/null +++ b/bot-core/src/main/java/dev/struchkov/bot/gitlab/core/service/parser/forktask/GetSingleIssueTask.java @@ -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> { + + private final String urlIssue; + private final long projectId; + private final long issueTwoId; + private final String gitlabToken; + + @Override + @SneakyThrows + protected Optional 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); + } + +} \ No newline at end of file diff --git a/bot-data/src/main/java/dev/struchkov/bot/gitlab/data/impl/IssueRepositoryImpl.java b/bot-data/src/main/java/dev/struchkov/bot/gitlab/data/impl/IssueRepositoryImpl.java new file mode 100644 index 0000000..84a8ae8 --- /dev/null +++ b/bot-data/src/main/java/dev/struchkov/bot/gitlab/data/impl/IssueRepositoryImpl.java @@ -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 findAllIdByStateIn(@NonNull Set statuses) { + return jpaRepository.findAllIdByStateIn(statuses); + } + + @Override + @Transactional + public Issue save(Issue issue) { + return jpaRepository.save(issue); + } + + @Override + @Transactional(readOnly = true) + public Optional findById(Long issueId) { + return jpaRepository.findById(issueId); + } + + @Override + @Transactional(readOnly = true) + public List findAllById(Set issueIds) { + return jpaRepository.findAllById(issueIds); + } + + @Override + @Transactional + public void deleteByStates(Set states) { + jpaRepository.deleteAllByStateIn(states); + } + +} \ No newline at end of file diff --git a/bot-data/src/main/java/dev/struchkov/bot/gitlab/data/jpa/IssueJpaRepository.java b/bot-data/src/main/java/dev/struchkov/bot/gitlab/data/jpa/IssueJpaRepository.java new file mode 100644 index 0000000..e6941ec --- /dev/null +++ b/bot-data/src/main/java/dev/struchkov/bot/gitlab/data/jpa/IssueJpaRepository.java @@ -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 { + + @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 findAllIdByStateIn(@Param("states") Set states); + + void deleteAllByStateIn(Set states); + +} \ No newline at end of file diff --git a/gitlab-app/src/main/java/dev/struchkov/bot/gitlab/scheduler/SchedulerService.java b/gitlab-app/src/main/java/dev/struchkov/bot/gitlab/scheduler/SchedulerService.java index f02c3da..14b3ae2 100644 --- a/gitlab-app/src/main/java/dev/struchkov/bot/gitlab/scheduler/SchedulerService.java +++ b/gitlab-app/src/main/java/dev/struchkov/bot/gitlab/scheduler/SchedulerService.java @@ -1,9 +1,11 @@ package dev.struchkov.bot.gitlab.scheduler; 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.PipelineService; 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.PipelineParser; import lombok.RequiredArgsConstructor; @@ -28,6 +30,9 @@ public class SchedulerService { private final PipelineService pipelineService; private final MergeRequestsService mergeRequestsService; + private final IssueParser issueParser; + private final IssueService issueService; + @Scheduled(cron = "0 */1 * * * *") public void newMergeRequest() { log.debug("Запуск процесса обновления данных"); @@ -40,6 +45,9 @@ public class SchedulerService { discussionParser.scanNewDiscussion(); mergeRequestsService.cleanOld(); pipelineService.cleanOld(); + issueParser.parsingNewIssue(); + issueParser.parsingOldIssue(); + issueService.cleanOld(); } else { log.warn("Процесс обновления данных не был выполнен, так как пользователь не выполнил первичную настройку."); } diff --git a/gitlab-app/src/main/resources/application.yml b/gitlab-app/src/main/resources/application.yml index f0a2367..91ce8ea 100644 --- a/gitlab-app/src/main/resources/application.yml +++ b/gitlab-app/src/main/resources/application.yml @@ -16,6 +16,8 @@ spring: jdbc: lob: non_contextual_creation: true + event: + merge.entity_copy_observer: allow telegram-config: bot-username: ${TELEGRAM_BOT_USERNAME} bot-token: ${TELEGRAM_BOT_TOKEN} @@ -50,4 +52,6 @@ gitlab-bot: last-commit-of-merge-request-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/commits?&page=1&per_page=1" 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,#}" - discussion-url: "${GITLAB_URL}/api/v4/projects/{0,number,#}/merge_requests/{1,number,#}/discussions/{2}" \ No newline at end of file + 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}" \ No newline at end of file diff --git a/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml b/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml index 7c2c0d8..d1dc525 100644 --- a/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml +++ b/gitlab-app/src/main/resources/liquibase/v.2.0.0/2023-01-19-create-tables-for-issue.xml @@ -77,6 +77,12 @@ + + + + + + diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java index 6d7863d..ee104c4 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/IssueJson.java @@ -3,11 +3,15 @@ package dev.struchkov.bot.gitlab.sdk.domain; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 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.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import lombok.Data; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.List; import java.util.Set; /** @@ -47,7 +51,7 @@ public class IssueJson { private Set labels; private MilestoneJson milestone; - private Set assignees; + private List assignees; private PersonJson author; private IssueTypeJson type; private PersonJson assignee; @@ -64,10 +68,10 @@ public class IssueJson { @JsonProperty("downvotes") private Integer downVotes; - @JsonSerialize(using = LocalDateTimeSerializer.class) - @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateSerializer.class) + @JsonDeserialize(using = LocalDateDeserializer.class) @JsonProperty("due_date") - private LocalDateTime dueDate; + private LocalDate dueDate; private Boolean confidential; @JsonProperty("discussion_locked") diff --git a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java index 33200ec..a46539b 100644 --- a/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java +++ b/gitlab-sdk/src/main/java/dev/struchkov/bot/gitlab/sdk/domain/MilestoneJson.java @@ -3,10 +3,13 @@ package dev.struchkov.bot.gitlab.sdk.domain; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 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.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import lombok.Data; +import java.time.LocalDate; import java.time.LocalDateTime; /** @@ -36,15 +39,15 @@ public class MilestoneJson { @JsonProperty("updated_at") private LocalDateTime updatedDate; - @JsonSerialize(using = LocalDateTimeSerializer.class) - @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateSerializer.class) + @JsonDeserialize(using = LocalDateDeserializer.class) @JsonProperty("start_date") - private LocalDateTime startDate; + private LocalDate startDate; - @JsonSerialize(using = LocalDateTimeSerializer.class) - @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonSerialize(using = LocalDateSerializer.class) + @JsonDeserialize(using = LocalDateDeserializer.class) @JsonProperty("due_date") - private LocalDateTime dueDate; + private LocalDate dueDate; private boolean expired; @JsonProperty("web_url") diff --git a/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DeleteFromAssigneesOfIssueNotifyGenerator.java b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DeleteFromAssigneesOfIssueNotifyGenerator.java new file mode 100644 index 0000000..92966c7 --- /dev/null +++ b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DeleteFromAssigneesOfIssueNotifyGenerator.java @@ -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 { + + @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; + } + +} \ No newline at end of file diff --git a/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DescriptionIssueNotifyGenerator.java b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DescriptionIssueNotifyGenerator.java new file mode 100644 index 0000000..67f612e --- /dev/null +++ b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DescriptionIssueNotifyGenerator.java @@ -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 { + + @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; + } + +} \ No newline at end of file diff --git a/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DueDateIssueNotifyGenerator.java b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DueDateIssueNotifyGenerator.java new file mode 100644 index 0000000..bda0e0e --- /dev/null +++ b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/DueDateIssueNotifyGenerator.java @@ -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 { + + @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; + } + +} \ No newline at end of file diff --git a/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/NewIssueNotifyGenerator.java b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/NewIssueNotifyGenerator.java new file mode 100644 index 0000000..bef4468 --- /dev/null +++ b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/NewIssueNotifyGenerator.java @@ -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 { + + @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; + } + +} \ No newline at end of file diff --git a/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/StatusIssueNotifyGenerator.java b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/StatusIssueNotifyGenerator.java new file mode 100644 index 0000000..2af1027 --- /dev/null +++ b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/StatusIssueNotifyGenerator.java @@ -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 { + + @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; + } + +} \ No newline at end of file diff --git a/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TitleIssueNotifyGenerator.java b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TitleIssueNotifyGenerator.java new file mode 100644 index 0000000..f41c257 --- /dev/null +++ b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TitleIssueNotifyGenerator.java @@ -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 { + + @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; + } + +} \ No newline at end of file diff --git a/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TypeIssueNotifyGenerator.java b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TypeIssueNotifyGenerator.java new file mode 100644 index 0000000..f9f0f5a --- /dev/null +++ b/telegram-bot/src/main/java/dev/struchkov/bot/gitlab/telegram/service/notify/TypeIssueNotifyGenerator.java @@ -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 { + + @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; + } + +} \ No newline at end of file