Кипит работа над версией 2

This commit is contained in:
upagge 2020-06-27 09:09:51 +03:00
parent 117f3ca443
commit 7b9388f1a4
No known key found for this signature in database
GPG Key ID: 15CD012E46F6BA34
89 changed files with 2092 additions and 1089 deletions

24
pom.xml
View File

@ -19,6 +19,30 @@
<dependencies>
<dependency>
<groupId>org.sadtech.basic.filter</groupId>
<artifactId>criteria-filter</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.sadtech.basic</groupId>
<artifactId>project-database</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>6.0.0.Alpha5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>

View File

@ -9,10 +9,18 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Общий файл настройки всего приложения.
*
* @author upagge
*/
@Configuration
@EnableScheduling
public class AppConfig {
/**
* Отвечает за работу шедулеров в паралельном режиме
*/
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();

View File

@ -1,23 +0,0 @@
package org.sadtech.bot.bitbucketbot.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* TODO: Добавить описание класса.
*
* @author upagge [31.01.2020]
*/
@Data
@Component
@ConfigurationProperties("bitbucketbot.bitbucket")
public class BitbucketConfig {
private String token;
private String urlPullRequestOpen;
private String urlPullRequestClose;
private String urlPullRequestComment;
private String urlPullRequest;
}

View File

@ -0,0 +1,49 @@
package org.sadtech.bot.bitbucketbot.config.properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Данные необходимые для взаимодействия с API Bitbucket.
*
* @author upagge [31.01.2020]
*/
@Getter
@Setter
@Component
@ConfigurationProperties("bitbucketbot.bitbucket")
public class BitbucketProperty {
/**
* Токен администратора
*/
private String token;
/**
* Адрес, по которому можно получить открытые ПР
*/
private String urlPullRequestOpen;
/**
* Адрес, по которому можно получить закрытые ПР
*/
private String urlPullRequestClose;
/**
* Адрес, по которому можно получить комментарии к ПР
*/
private String urlPullRequestComment;
/**
* Адрес ПР
*/
private String urlPullRequest;
/**
* Адрес на получение пользователей битбакет
*/
private String urlUsers;
}

View File

@ -0,0 +1,30 @@
package org.sadtech.bot.bitbucketbot.config.properties;
import lombok.Getter;
import lombok.Setter;
import org.sadtech.bot.bitbucketbot.scheduler.SchedulerComments;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* Данные для конфигурации {@link SchedulerComments}
*
* @author upagge
*/
@Getter
@Setter
@Component
@ConfigurationProperties("bitbucketbot.scheduler.comment.settings")
public class CommentSchedulerProperty {
/**
* Количество пустых комментариев подряд, после которого поиск останавливается
*/
private Integer noCommentCount;
/**
* Количество комментариев в пачке сканирования
*/
private Integer commentCount;
}

View File

@ -1,9 +1,9 @@
package org.sadtech.bot.bitbucketbot.controller;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.dto.UserDto;
import org.sadtech.bot.bitbucketbot.service.UserService;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PostMapping;
@ -12,16 +12,21 @@ import org.springframework.web.bind.annotation.RestController;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* Контроллер отвечат за регистрацию пользователей.
*
* @author upagge
*/
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private final PersonService personService;
private final ConversionService conversionService;
@PostMapping(value = "/api/user/reg", consumes = APPLICATION_JSON_VALUE)
public HttpStatus register(@RequestBody UserDto userDto) {
userService.reg(conversionService.convert(userDto, User.class));
personService.reg(conversionService.convert(userDto, Person.class));
return HttpStatus.OK;
}

View File

@ -5,6 +5,8 @@ package org.sadtech.bot.bitbucketbot.domain;
*/
public enum PullRequestStatus {
OPEN, MERGED, DECLINED, DELETE
OPEN,
MERGED,
DECLINED
}

View File

@ -0,0 +1,8 @@
package org.sadtech.bot.bitbucketbot.domain;
public enum TaskStatus {
OPEN,
RESOLVED
}

View File

@ -8,13 +8,15 @@ import java.time.LocalDateTime;
import java.util.Set;
@Getter
@EqualsAndHashCode(of = "id")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public abstract class Change {
protected final ChangeType type;
protected final LocalDateTime localDateTime = LocalDateTime.now();
protected final Set<Long> telegramIds;
@Setter
@EqualsAndHashCode.Include
protected Long id;
protected Change(ChangeType type, Set<Long> telegramIds) {

View File

@ -8,6 +8,10 @@ public enum ChangeType {
NEW_PR,
CONFLICT_PR,
NEW_COMMENT,
NEW_ANSWERS_COMMENT
NEW_ANSWERS_COMMENT,
NEW_TASK,
DELETED_TASK,
RESOLVED_TASK,
OPEN_TASK;
}

View File

@ -1,9 +1,11 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.comment;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.Answer;
import org.sadtech.bot.bitbucketbot.domain.change.Change;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.List;
import java.util.Set;

View File

@ -1,8 +1,10 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.comment;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.change.Change;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.Set;

View File

@ -1,7 +1,8 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.pullrequest;
import lombok.Builder;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.Set;

View File

@ -1,8 +1,9 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.pullrequest;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.Set;
@ -16,11 +17,11 @@ public class NewPrChange extends PrChange {
@Builder
private NewPrChange(
Set<Long> telegramIds,
String name,
String title,
String url,
String description,
String author) {
super(ChangeType.NEW_PR, telegramIds, name, url);
super(ChangeType.NEW_PR, telegramIds, title, url);
this.description = description;
this.author = author;
}

View File

@ -1,7 +1,9 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.pullrequest;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.change.Change;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.Set;
@ -9,12 +11,12 @@ import java.util.Set;
@EqualsAndHashCode(callSuper = true)
public abstract class PrChange extends Change {
private final String name;
private final String title;
private final String url;
protected PrChange(ChangeType type, Set<Long> telegramIds, String name, String url) {
protected PrChange(ChangeType type, Set<Long> telegramIds, String title, String url) {
super(type, telegramIds);
this.name = name;
this.title = title;
this.url = url;
}

View File

@ -1,12 +1,13 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.pullrequest;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import org.sadtech.bot.bitbucketbot.domain.util.ReviewerChange;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@Getter
@EqualsAndHashCode(callSuper = true)
@ -16,11 +17,11 @@ public class ReviewersPrChange extends PrChange {
@Builder
private ReviewersPrChange(
Set<Long> telegramIds,
String name,
Long telegramId,
String title,
String url,
List<ReviewerChange> reviewerChanges) {
super(ChangeType.REVIEWERS, telegramIds, name, url);
super(ChangeType.REVIEWERS, Collections.singleton(telegramId), title, url);
this.reviewerChanges = reviewerChanges;
}

View File

@ -1,9 +1,10 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.pullrequest;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.Set;

View File

@ -1,8 +1,9 @@
package org.sadtech.bot.bitbucketbot.domain.change;
package org.sadtech.bot.bitbucketbot.domain.change.pullrequest;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.Set;

View File

@ -0,0 +1,32 @@
package org.sadtech.bot.bitbucketbot.domain.change.task;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.sadtech.bot.bitbucketbot.domain.change.Change;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import java.util.Set;
@Getter
@EqualsAndHashCode(callSuper = true)
public class TaskChange extends Change {
protected final String authorName;
protected final String url;
protected final String messageTask;
@Builder
protected TaskChange(
ChangeType type,
Set<Long> telegramIds,
String authorName,
String url,
String messageTask
) {
super(type, telegramIds);
this.authorName = authorName;
this.url = url;
this.messageTask = messageTask;
}
}

View File

@ -1,48 +1,58 @@
package org.sadtech.bot.bitbucketbot.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import java.time.LocalDateTime;
import java.util.Set;
@Getter
@Setter
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of = "id")
@ToString
@Table(name = "pull_request_comment")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Comment {
/**
* Идентификатор
*/
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include
private Long id;
@Column(name = "url")
private String url;
@Column(name = "pr_url")
private String prUrl;
@Column(name = "pull_request_id")
private Long pullRequestId;
@Column(name = "telegram")
private Long telegram;
@Column(name = "author_login")
private String author;
@Column(name = "date")
private LocalDateTime date;
@Column(name = "message")
private String message;
@Column(name = "createDate")
private LocalDateTime createDate;
/**
* Версия объекта в битбакет
*/
@Column(name = "bitbucket_version")
private Integer bitbucketVersion;
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "comment_tree", joinColumns = @JoinColumn(name = "parent_id"))

View File

@ -0,0 +1,59 @@
package org.sadtech.bot.bitbucketbot.domain.entity;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* Пользователь битбакета.
*
* @author upagge [30.01.2020]
*/
@Getter
@Setter
@Entity
@Table(name = "person")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Person {
/**
* Логин
*/
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include
private Long id;
/**
* Логин
*/
@Column(name = "login")
private String login;
/**
* Персональный токен из битбакета
*/
@Column(name = "bitbucket_token")
private String token;
/**
* Идентификатор телеграма
*/
@Column(name = "telegram_id")
private Long telegramId;
/**
* ФИО
*/
@Column(name = "full_name")
private String fullName;
}

View File

@ -1,12 +1,8 @@
package org.sadtech.bot.bitbucketbot.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import javax.persistence.CascadeType;
@ -19,74 +15,122 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.time.LocalDateTime;
import java.util.List;
/**
* TODO: Добавить описание класса.
* Сущность ПуллРеквест.
*
* @author upagge [31.01.2020]
*/
@Getter
@Setter
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of = "id")
@ToString
@Table(name = "pull_request")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class PullRequest {
/**
* Идентификатор
*/
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@EqualsAndHashCode.Include
private Long id;
/**
* Идентификатор на стороне битбакета
*/
@Column(name = "bitbucket_pr_id")
private Long bitbucketId;
/**
* Идентификатор репозитория на стороне битбакета
*/
@Column(name = "repository_id")
private Long repositoryId;
/**
* Идентификатор проекта на стороне битбакета
*/
@Column(name = "project_key")
private String projectKey;
/**
* Символьный идентификатор на стороне битбакета
*/
@Column(name = "repository_slug")
private String repositorySlug;
/**
* Версия объекта для блокировок
*/
@Column(name = "version")
private Integer version;
/**
* Описание пулреквеста
*/
@Column(name = "description")
private String description;
@ManyToOne
@JoinColumn(name = "author_login")
private User author;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(name = "pull_request_id")
private List<Reviewer> reviewers;
/**
* Адрес ПР
*/
@Column(name = "url")
private String url;
@Column(name = "name")
private String name;
/**
* Название ПР
*/
@Column(name = "title")
private String title;
/**
* Статус ПР
*/
@Enumerated(EnumType.STRING)
@Column(name = "status")
private PullRequestStatus status;
/**
* Дата создания
*/
@Column(name = "create_date")
private LocalDateTime createDate;
/**
* Дата обновления
*/
@Column(name = "update_date")
private LocalDateTime updateDate;
/**
* Флаг показывающий наличие конфликта в ПР
*/
@Column(name = "conflict")
private boolean conflict;
/**
* Версия объекта в битбакет
*/
@Column(name = "bitbucket_version")
private Integer bitbucketVersion;
/**
* Автор ПР
*/
@Column(name = "author_login")
private String authorLogin;
/**
* Ревьюверы
*/
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(name = "pull_request_id")
private List<Reviewer> reviewers;
}

View File

@ -1,9 +1,7 @@
package org.sadtech.bot.bitbucketbot.domain.entity;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
@ -17,27 +15,35 @@ import javax.persistence.Id;
import javax.persistence.Table;
/**
* TODO: Добавить описание класса.
* Ревьювер пулреквеста.
*
* @author upagge [01.02.2020]
*/
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "reviewer")
@EqualsAndHashCode(of = "id")
@Table(name = "pull_request_reviewer")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Reviewer {
/**
* Идентификатор
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@EqualsAndHashCode.Include
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* Пользователь
*/
@Column(name = "user_login")
private String user;
private String userLogin;
/**
* Статус
*/
@Enumerated(EnumType.STRING)
@Column(name = "status")
private ReviewerStatus status;

View File

@ -0,0 +1,69 @@
package org.sadtech.bot.bitbucketbot.domain.entity;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.sadtech.bot.bitbucketbot.domain.TaskStatus;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
@Table(name = "pull_request_task")
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Task {
/**
* Идентификатор
*/
@Id
@Column(name = "id")
@EqualsAndHashCode.Include
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* Описание задачи
*/
@Column(name = "description")
private String description;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private TaskStatus status;
@Column(name = "create_date")
private LocalDateTime createDate;
@OneToMany
@Column(name = "pull_request_id")
private Long pullRequestId;
@Column(name = "url")
private String url;
@Column(name = "url_api")
private String urlApi;
/**
* Версия объекта в битбакет
*/
@Column(name = "bitbucket_version")
private Integer bitbucketVersion;
@OneToMany
@JoinColumn(name = "author_login")
private Person author;
}

View File

@ -1,35 +0,0 @@
package org.sadtech.bot.bitbucketbot.domain.entity;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Getter
@Setter
@Entity
@Table(name = "tech_info")
@EqualsAndHashCode(of = "surogatId")
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class TechInfo {
@Id
@Column(name = "surogat_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long surogatId;
@Column(name = "last_comment_id")
private Long lastCommentId;
}

View File

@ -1,45 +0,0 @@
package org.sadtech.bot.bitbucketbot.domain.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* TODO: Добавить описание класса.
*
* @author upagge [30.01.2020]
*/
@Builder
@Getter
@Setter
@Entity
@Table(name = "`user`")
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "login")
@ToString
public class User {
@Id
@Column(name = "login")
private String login;
@Column(name = "token")
private String token;
@Column(name = "telegram_id")
private Long telegramId;
@Column(name = "full_name")
private String fullName;
}

View File

@ -0,0 +1,20 @@
package org.sadtech.bot.bitbucketbot.domain.filter;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class PullRequestFilter {
private Long bitbucketId;
private Long bitbucketRepositoryId;
}

View File

@ -11,10 +11,15 @@ import java.util.List;
public class CommentJson {
private Long id;
private Integer version;
private String text;
private UserJson author;
private List<CommentJson> comments;
private Severity severity;
private CommentState state;
@JsonDeserialize(using = LocalDateTimeFromEpochDeserializer.class)
private LocalDateTime createdDate;

View File

@ -0,0 +1,8 @@
package org.sadtech.bot.bitbucketbot.dto.bitbucket;
public enum CommentState {
RESOLVED,
OPEN
}

View File

@ -0,0 +1,8 @@
package org.sadtech.bot.bitbucketbot.dto.bitbucket;
public enum Severity {
NORMAL,
BLOCKER
}

View File

@ -1,9 +1,13 @@
package org.sadtech.bot.bitbucketbot.exception;
class BitbucketBotException extends RuntimeException {
abstract class BitbucketBotException extends RuntimeException {
public BitbucketBotException(String message) {
protected BitbucketBotException(String message) {
super(message);
}
protected BitbucketBotException(String message, Throwable throwable) {
super(message, throwable);
}
}

View File

@ -0,0 +1,9 @@
package org.sadtech.bot.bitbucketbot.exception;
public class CreateException extends BitbucketBotException {
public CreateException(String message) {
super(message);
}
}

View File

@ -0,0 +1,9 @@
package org.sadtech.bot.bitbucketbot.exception;
public class UpdateException extends BitbucketBotException {
public UpdateException(String message) {
super(message);
}
}

View File

@ -0,0 +1,20 @@
package org.sadtech.bot.bitbucketbot.repository;
import org.sadtech.basic.context.repository.BusinessLogicRepository;
import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import java.util.List;
import java.util.Set;
public interface PullRequestsRepository extends BusinessLogicRepository<PullRequest, Long> {
List<PullRequest> findAllByReviewerAndStatuses(String login, ReviewerStatus reviewerStatus, Set<PullRequestStatus> statuses);
List<PullRequest> findAllByAuthorAndReviewerStatus(String login, ReviewerStatus status);
Set<IdAndStatusPr> findAllIdByStatusIn(Set<PullRequestStatus> statuses);
}

View File

@ -0,0 +1,18 @@
package org.sadtech.bot.bitbucketbot.repository;
import lombok.NonNull;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import java.util.Optional;
public interface TaskRepository {
Task save(@NonNull Task task);
void deleteById(@NonNull Long id);
Optional<Task> findById(@NonNull Long id);
Optional<Task> findFirstByOrderByIdDesc();
}

View File

@ -0,0 +1,40 @@
package org.sadtech.bot.bitbucketbot.repository.impl;
import org.sadtech.basic.database.repository.AbstractBusinessLogicJpaRepository;
import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.repository.PullRequestsRepository;
import org.sadtech.bot.bitbucketbot.repository.jpa.PullRequestsRepositoryJpa;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Set;
@Repository
public class PullRequestsRepositoryImpl extends AbstractBusinessLogicJpaRepository<PullRequest, Long> implements PullRequestsRepository {
private final PullRequestsRepositoryJpa pullRequestsRepositoryJpa;
protected PullRequestsRepositoryImpl(PullRequestsRepositoryJpa pullRequestsRepositoryJpa) {
super(pullRequestsRepositoryJpa);
this.pullRequestsRepositoryJpa = pullRequestsRepositoryJpa;
}
@Override
public List<PullRequest> findAllByReviewerAndStatuses(String login, ReviewerStatus reviewerStatus, Set<PullRequestStatus> statuses) {
return pullRequestsRepositoryJpa.findAllByReviewerAndStatuses(login, reviewerStatus, statuses);
}
@Override
public List<PullRequest> findAllByAuthorAndReviewerStatus(String login, ReviewerStatus status) {
return pullRequestsRepositoryJpa.findAllByAuthorAndReviewerStatus(login, status);
}
@Override
public Set<IdAndStatusPr> findAllIdByStatusIn(Set<PullRequestStatus> statuses) {
return pullRequestsRepositoryJpa.findAllIdByStatusIn(statuses);
}
}

View File

@ -0,0 +1,33 @@
package org.sadtech.bot.bitbucketbot.repository.impl;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import org.sadtech.bot.bitbucketbot.repository.TaskRepository;
import org.sadtech.bot.bitbucketbot.repository.jpa.TaskRepositoryJpa;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
@RequiredArgsConstructor
public class TaskRepositoryImpl implements TaskRepository {
private final TaskRepositoryJpa taskRepositoryJpa;
@Override
public Task save(@NonNull Task task) {
return taskRepositoryJpa.save(task);
}
@Override
public void deleteById(@NonNull Long id) {
taskRepositoryJpa.deleteById(id);
}
@Override
public Optional<Task> findById(@NonNull Long id) {
return taskRepositoryJpa.findById(id);
}
}

View File

@ -1,6 +1,6 @@
package org.sadtech.bot.bitbucketbot.repository.jpa;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@ -15,20 +15,22 @@ import java.util.Set;
* @author upagge [30.01.2020]
*/
@Repository
public interface UserRepository extends JpaRepository<User, String> {
public interface PersonRepository extends JpaRepository<Person, String> {
boolean existsByTelegramId(Long chatId);
boolean existsByLogin(String login);
List<User> findAllByTelegramIdNotNullAndTokenNotNull();
List<Person> findAllByTelegramIdNotNullAndTokenNotNull();
@Query("SELECT u.telegramId FROM User u WHERE u.login=:login")
@Query("SELECT u.telegramId FROM Person u WHERE u.login=:login")
Long findTelegramIdByLogin(String login);
@Query("SELECT u.telegramId FROM User u WHERE u.login IN :logins AND u.telegramId IS NOT NULL")
@Query("SELECT u.telegramId FROM Person u WHERE u.login IN :logins AND u.telegramId IS NOT NULL")
Set<Long> findAllTelegramIdByLogin(Set<String> logins);
Optional<User> findByLogin(String login);
Optional<Person> findByLogin(String login);
Person getByLogin(String login);
}

View File

@ -4,8 +4,8 @@ import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.data.repository.query.Param;
import java.time.LocalDateTime;
@ -17,7 +17,7 @@ import java.util.Set;
/**
* @author upagge [31.01.2020]
*/
public interface PullRequestsRepository extends JpaRepository<PullRequest, Long> {
public interface PullRequestsRepositoryJpa extends JpaRepositoryImplementation<PullRequest, Long> {
Set<PullRequest> findAllByIdIn(Set<Long> ids);
@ -34,7 +34,7 @@ public interface PullRequestsRepository extends JpaRepository<PullRequest, Long>
@Query("SELECT p FROM PullRequest p LEFT JOIN p.reviewers r WHERE p.author.login=:author AND r.status=:reviewerStatus")
List<PullRequest> findAllByAuthorAndReviewerStatus(@Param("author") String author, @Param("reviewerStatus") ReviewerStatus reviewerStatus);
@Query("SELECT new org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr(p.id, p.status) FROM PullRequest p WHERE p.status IN :statuses")
// @Query("SELECT new org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr(p.id, p.status) FROM PullRequest p WHERE p.status IN :statuses")
Set<IdAndStatusPr> findAllIdByStatusIn(@Param("statuses") Set<PullRequestStatus> statuses);
@Query("SELECT p.id from PullRequest p")

View File

@ -0,0 +1,8 @@
package org.sadtech.bot.bitbucketbot.repository.jpa;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TaskRepositoryJpa extends JpaRepository<Task, Long> {
}

View File

@ -3,14 +3,15 @@ package org.sadtech.bot.bitbucketbot.scheduler;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.domain.MessageSend;
import org.sadtech.bot.bitbucketbot.domain.change.AnswerCommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.Change;
import org.sadtech.bot.bitbucketbot.domain.change.CommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.ConflictPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.NewPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.ReviewersPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.StatusPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.UpdatePrChange;
import org.sadtech.bot.bitbucketbot.domain.change.comment.AnswerCommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.comment.CommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ConflictPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.NewPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ReviewersPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.StatusPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.UpdatePrChange;
import org.sadtech.bot.bitbucketbot.domain.change.task.TaskChange;
import org.sadtech.bot.bitbucketbot.exception.NotFoundException;
import org.sadtech.bot.bitbucketbot.service.ChangeService;
import org.sadtech.bot.bitbucketbot.service.MessageSendService;
@ -21,6 +22,12 @@ import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* Парсер изменений. Отслеживает изменения, которые были добавлены и добавляет событие на отправку уведомления
* пользователю.
*
* @author upagge
*/
@Service
@RequiredArgsConstructor
public class SchedulerChangeParsing {
@ -28,6 +35,10 @@ public class SchedulerChangeParsing {
private final MessageSendService messageSendService;
private final ChangeService changeService;
/**
* Проверяет наличие новых изменений. Если изменения найдены, то создает новое сообщение и отправляет
* его в сервис отправки сообщений {@link MessageSendService}
*/
@Scheduled(cron = "*/15 * * * * *")
public void parsing() {
final List<Change> newChange = changeService.getNew().stream()
@ -46,6 +57,12 @@ public class SchedulerChangeParsing {
}
}
/**
* Создает сообщение, которое необходимо отправить в зависимости от типа изменения.
*
* @param change Объект изменения
* @return Текстовое сообщение
*/
private String generateMessage(@NonNull Change change) {
String message;
switch (change.getType()) {
@ -70,6 +87,16 @@ public class SchedulerChangeParsing {
case NEW_ANSWERS_COMMENT:
message = Message.generate(((AnswerCommentChange) change));
break;
case NEW_TASK:
case OPEN_TASK:
message = Message.generateNewTask(((TaskChange) change));
break;
case DELETED_TASK:
message = Message.generateDeleteTask(((TaskChange) change));
break;
case RESOLVED_TASK:
message = Message.generateResolveTask(((TaskChange) change));
break;
default:
throw new NotFoundException("Нет обработчика для типа " + change.getType().name());
}

View File

@ -3,22 +3,32 @@ package org.sadtech.bot.bitbucketbot.scheduler;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sadtech.bot.bitbucketbot.config.BitbucketConfig;
import org.sadtech.bot.bitbucketbot.config.properties.BitbucketProperty;
import org.sadtech.bot.bitbucketbot.config.properties.CommentSchedulerProperty;
import org.sadtech.bot.bitbucketbot.domain.Answer;
import org.sadtech.bot.bitbucketbot.domain.Pagination;
import org.sadtech.bot.bitbucketbot.domain.change.AnswerCommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.CommentChange;
import org.sadtech.bot.bitbucketbot.domain.TaskStatus;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import org.sadtech.bot.bitbucketbot.domain.change.comment.AnswerCommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.comment.CommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.task.TaskChange;
import org.sadtech.bot.bitbucketbot.domain.entity.Comment;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.Severity;
import org.sadtech.bot.bitbucketbot.exception.NotFoundException;
import org.sadtech.bot.bitbucketbot.service.ChangeService;
import org.sadtech.bot.bitbucketbot.service.CommentService;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.sadtech.bot.bitbucketbot.service.PullRequestsService;
import org.sadtech.bot.bitbucketbot.service.UserService;
import org.sadtech.bot.bitbucketbot.service.TaskService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.sadtech.bot.bitbucketbot.service.executor.DataScan;
import org.sadtech.bot.bitbucketbot.service.executor.ResultScan;
import org.sadtech.bot.bitbucketbot.service.impl.ExecutorScanner;
import org.sadtech.bot.bitbucketbot.utils.Converter;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@ -34,125 +44,219 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Шедулер отвечает за работу с комментариями. Поиск новых комментариев, проверка старых. Так как таски в
* битбакете реализуются через комментарии, то <b>этот шедулер так же работает с тасками</b>.
*
* @author upagge
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SchedulerComments {
private static final Integer COUNT = 100;
private static final Integer NO_COMMENT = 20;
private static final Pattern PATTERN = Pattern.compile("@[\\w]+");
private final CommentService commentService;
private final PullRequestsService pullRequestsService;
private final UserService userService;
private final PersonService personService;
private final ChangeService changeService;
private final ExecutorScanner executorScanner;
private final TaskService taskService;
private final ConversionService conversionService;
private final BitbucketConfig bitbucketConfig;
private final BitbucketProperty bitbucketProperty;
private final CommentSchedulerProperty commentSchedulerProperty;
@Scheduled(cron = "0 */3 * * * MON-FRI")
/**
* Сканирует появление новых комментариев
*/
@Scheduled(cron = "0 */3 * * * *")
public void newComments() {
long commentId = commentService.getLastCommentId() + 1;
int count = 0;
do {
List<DataScan> commentUrls = new ArrayList<>();
for (int i = 0; i < 5; i++) {
int page = 0;
Page<PullRequest> pullRequestPage = pullRequestsService.getAll(Pagination.of(page, COUNT));
while (pullRequestPage.hasContent()) {
long finalCommentId = commentId;
commentUrls.addAll(pullRequestPage.getContent().stream()
.map(pullRequest -> new DataScan(getCommentUrl(finalCommentId, pullRequest), pullRequest.getUrl()))
.collect(Collectors.toList()));
pullRequestPage = pullRequestsService.getAll(Pagination.of(++page, COUNT));
}
commentId++;
}
executorScanner.registration(commentUrls);
final List<ResultScan> result = executorScanner.getResult();
if (!result.isEmpty()) {
result.forEach(resultScan -> {
notificationPersonal(resultScan.getCommentJson(), resultScan.getUrlPr());
saveComments(resultScan.getCommentJson(), resultScan.getUrlComment(), resultScan.getUrlPr());
});
final List<DataScan> dataScans = generatingLinksToPossibleComments(commentId);
executorScanner.registration(dataScans);
final List<ResultScan> resultScans = executorScanner.getResult();
if (!resultScans.isEmpty()) {
final List<Comment> comments = getCommentsByResultScan(resultScans);
final List<Comment> newComments = commentService.createAll(comments);
checkNewTask(newComments);
notificationPersonal(newComments);
count = 0;
}
} while (count++ < NO_COMMENT);
} while (count++ < commentSchedulerProperty.getNoCommentCount());
}
@Scheduled(cron = "0 */1 * * * MON-FRI")
private List<Comment> getCommentsByResultScan(List<ResultScan> resultScans) {
return resultScans.stream()
.map(result -> conversionService.convert(result, Comment.class))
.collect(Collectors.toList());
}
private List<DataScan> generatingLinksToPossibleComments(@NonNull Long commentId) {
List<DataScan> commentUrls = new ArrayList<>();
for (int i = 0; i < 5; i++) {
int page = 0;
Page<PullRequest> pullRequestPage = pullRequestsService.getAll(
Pagination.of(page, commentSchedulerProperty.getCommentCount())
);
while (pullRequestPage.hasContent()) {
long finalCommentId = commentId;
commentUrls.addAll(pullRequestPage.getContent().stream()
.map(
pullRequest -> new DataScan(
getCommentUrl(finalCommentId, pullRequest),
pullRequest.getId()
)
)
.collect(Collectors.toList()));
pullRequestPage = pullRequestsService.getAll(
Pagination.of(++page, commentSchedulerProperty.getCommentCount())
);
}
commentId++;
}
return commentUrls;
}
private void checkNewTask(CommentJson commentJson, String urlPr, String authorLoginPr) {
if (Severity.BLOCKER.equals(commentJson.getSeverity())) {
final Task task = new Task();
task.setStatus(Converter.taskStatus(commentJson.getState()));
task.setComment(commentService.getProxyById(commentJson.getId()).orElseThrow(() -> new NotFoundException("Неожиданная ошибка")));
taskService.create(task);
if (TaskStatus.OPEN.equals(task.getStatus())) {
changeService.add(
TaskChange.builder()
.type(ChangeType.NEW_TASK)
.authorName(commentJson.getAuthor().getDisplayName())
.messageTask(commentJson.getText())
.telegramIds(personService.getAllTelegramIdByLogin(Collections.singleton(authorLoginPr)))
.url(urlPr)
.build()
);
}
}
}
/**
* Проверяет состояние старых комментариев
*/
@Scheduled(cron = "0 */1 * * * *")
public void oldComments() {
@NonNull final List<Comment> comments = commentService.getAllBetweenDate(LocalDateTime.now().minusDays(10), LocalDateTime.now());
@NonNull final List<Comment> comments = commentService.getAllBetweenDate(
LocalDateTime.now().minusDays(10), LocalDateTime.now()
);
for (Comment comment : comments) {
final Optional<CommentJson> optCommentJson = Utils.urlToJson(
comment.getUrl(),
bitbucketConfig.getToken(),
bitbucketProperty.getToken(),
CommentJson.class
);
if (optCommentJson.isPresent()) {
final CommentJson commentJson = optCommentJson.get();
final Set<Long> oldAnswerIds = comment.getAnswers();
final List<CommentJson> newAnswers = commentJson.getComments().stream()
.filter(answerJson -> !oldAnswerIds.contains(answerJson.getId()))
.collect(Collectors.toList());
if (!newAnswers.isEmpty()) {
checkNewAnswers(comment, commentJson);
checkOldTask(comment, commentJson);
}
}
}
private void checkOldTask(Comment comment, CommentJson commentJson) {
final Task task = comment.getTask();
if (task == null) {
checkNewTask(commentJson, comment.getPrUrl(), commentJson.getAuthor().getName());
} else {
if (Severity.NORMAL.equals(commentJson.getSeverity())) {
taskService.deleteById(comment.getId());
changeService.add(
TaskChange.builder()
.type(ChangeType.DELETED_TASK)
.telegramIds(personService.getAllTelegramIdByLogin(Collections.singleton(commentJson.getAuthor().getName())))
.authorName(commentJson.getAuthor().getDisplayName())
.url(comment.getPrUrl())
.messageTask(commentJson.getText())
.build()
);
} else {
final TaskStatus taskStatus = task.getStatus();
final TaskStatus newTaskStatus = Converter.taskStatus(commentJson.getState());
task.setStatus(newTaskStatus);
taskService.update(task);
if (!taskStatus.equals(newTaskStatus)) {
changeService.add(
AnswerCommentChange.builder()
.telegramIds(
userService.getTelegramIdByLogin(commentJson.getAuthor().getName())
.map(Collections::singleton)
.orElse(Collections.emptySet())
)
TaskChange.builder()
.type(TaskStatus.RESOLVED.equals(newTaskStatus) ? ChangeType.RESOLVED_TASK : ChangeType.OPEN_TASK)
.authorName(commentJson.getAuthor().getDisplayName())
.url(comment.getPrUrl())
.youMessage(commentJson.getText())
.answers(
newAnswers.stream()
.map(json -> Answer.of(json.getAuthor().getName(), json.getText()))
.collect(Collectors.toList())
.messageTask(commentJson.getText())
.telegramIds(
TaskStatus.RESOLVED.equals(newTaskStatus)
? personService.getAllTelegramIdByLogin(Collections.singleton(commentJson.getAuthor().getName()))
: personService.getAllTelegramIdByLogin(Collections.singleton(commentJson.getAuthor().getName()))
)
.build()
);
comment.getAnswers().addAll(newAnswers.stream().map(CommentJson::getId).collect(Collectors.toList()));
commentService.save(comment);
}
}
}
}
@NonNull
private void saveComments(CommentJson comment, String commentUrl, String prUrl) {
final Comment newComment = new Comment();
newComment.setId(comment.getId());
newComment.setDate(LocalDateTime.now());
newComment.setUrl(commentUrl);
newComment.setPrUrl(prUrl);
userService.getTelegramIdByLogin(comment.getAuthor().getName()).ifPresent(newComment::setTelegram);
commentService.save(newComment);
private void checkNewAnswers(Comment comment, CommentJson commentJson) {
final Set<Long> oldAnswerIds = comment.getAnswers();
final List<CommentJson> newAnswers = commentJson.getComments().stream()
.filter(answerJson -> !oldAnswerIds.contains(answerJson.getId()))
.collect(Collectors.toList());
if (!newAnswers.isEmpty()) {
changeService.add(
AnswerCommentChange.builder()
.telegramIds(
personService.getTelegramIdByLogin(commentJson.getAuthor().getName())
.map(Collections::singleton)
.orElse(Collections.emptySet())
)
.url(comment.getPrUrl())
.youMessage(commentJson.getText())
.answers(
newAnswers.stream()
.map(json -> Answer.of(json.getAuthor().getName(), json.getText()))
.collect(Collectors.toList())
)
.build()
);
comment.getAnswers().addAll(newAnswers.stream().map(CommentJson::getId).collect(Collectors.toList()));
commentService.save(comment);
}
}
private String getCommentUrl(long commentId, PullRequest pullRequest) {
return bitbucketConfig.getUrlPullRequestComment()
return bitbucketProperty.getUrlPullRequestComment()
.replace("{projectKey}", pullRequest.getProjectKey())
.replace("{repositorySlug}", pullRequest.getRepositorySlug())
.replace("{pullRequestId}", pullRequest.getBitbucketId().toString())
.replace("{commentId}", String.valueOf(commentId));
}
private void notificationPersonal(@NonNull CommentJson comment, @NonNull String urlPr) {
Matcher matcher = PATTERN.matcher(comment.getText());
private void notificationPersonal(@NonNull Comment comment) {
Matcher matcher = PATTERN.matcher(comment.getMessage());
Set<String> recipientsLogins = new HashSet<>();
while (matcher.find()) {
final String login = matcher.group(0).replace("@", "");
recipientsLogins.add(login);
}
final Set<Long> recipientsIds = userService.getAllTelegramIdByLogin(recipientsLogins);
final Set<Long> recipientsIds = personService.getAllTelegramIdByLogin(recipientsLogins);
changeService.add(
CommentChange.builder()
.authorName(comment.getAuthor().getName())
.url(urlPr)
.authorName(comment.getAuthor().getLogin())
.url(comment.getPullRequest())
.telegramIds(recipientsIds)
.message(comment.getText())
.message(comment.getMessage())
.build()
);
}

View File

@ -1,56 +0,0 @@
package org.sadtech.bot.bitbucketbot.scheduler;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.config.BitbucketConfig;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.UserJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.sheet.UserSheetJson;
import org.sadtech.bot.bitbucketbot.service.UserService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.springframework.core.convert.ConversionService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* TODO: Добавить описание класса.
*
* @author upagge [02.02.2020]
*/
@Service
@RequiredArgsConstructor
public class SchedulerNewUser {
private static final String URL = "http://192.168.236.164:7990/rest/api/1.0/admin/users";
private final UserService userService;
private final ConversionService conversionService;
private final BitbucketConfig bitbucketConfig;
@Scheduled(fixedRate = 86400000)
private void scan() {
Optional<UserSheetJson> sheetJson = Utils.urlToJson(URL, bitbucketConfig.getToken(), UserSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().getValues()!=null && !sheetJson.get().getValues().isEmpty()) {
final UserSheetJson sheetUsers = sheetJson.get();
final List<UserJson> users = sheetUsers.getValues();
final Set<String> logins = users.stream().map(UserJson::getName).collect(Collectors.toSet());
final Set<String> existsLogins = userService.existsByLogin(logins);
final Set<User> newUsers = users.stream()
.filter(userJson -> !existsLogins.contains(userJson.getName()))
.map(userJson -> conversionService.convert(userJson, User.class))
.collect(Collectors.toSet());
if (!newUsers.isEmpty()) {
userService.addAll(newUsers);
}
if (sheetUsers.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(URL + sheetUsers.getNextPageStart(), bitbucketConfig.getToken(), UserSheetJson.class);
} else {
break;
}
}
}
}

View File

@ -4,12 +4,12 @@ import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.domain.MessageSend;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.service.MessageSendService;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.sadtech.bot.bitbucketbot.service.PullRequestsService;
import org.sadtech.bot.bitbucketbot.service.ReportService;
import org.sadtech.bot.bitbucketbot.service.UserService;
import org.sadtech.bot.bitbucketbot.utils.Message;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@ -24,7 +24,7 @@ public class SchedulerNotification {
private static final Set<PullRequestStatus> statuses = Collections.singleton(PullRequestStatus.OPEN);
private final UserService userService;
private final PersonService personService;
private final PullRequestsService pullRequestsService;
private final MessageSendService messageSendService;
private final ReportService reportService;
@ -32,8 +32,8 @@ public class SchedulerNotification {
// Утреннее сообщение
@Scheduled(cron = "0 15 8 * * MON-FRI")
public void goodMorning() {
List<User> allRegister = userService.getAllRegister();
for (User user : allRegister) {
List<Person> allRegister = personService.getAllRegister();
for (Person user : allRegister) {
List<PullRequest> pullRequestsReviews = pullRequestsService.getAllByReviewerAndStatuses(
user.getLogin(),
ReviewerStatus.NEEDS_WORK,
@ -51,8 +51,8 @@ public class SchedulerNotification {
@Scheduled(cron = "0 0 18 * * FRI")
public void goodWeekEnd() {
List<User> allRegister = userService.getAllRegister();
for (User user : allRegister) {
List<Person> allRegister = personService.getAllRegister();
for (Person user : allRegister) {
messageSendService.add(
MessageSend.builder()
.telegramId(user.getTelegramId())

View File

@ -1,320 +0,0 @@
package org.sadtech.bot.bitbucketbot.scheduler;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sadtech.bot.bitbucketbot.config.BitbucketConfig;
import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.change.ConflictPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.NewPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.ReviewersPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.StatusPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.UpdatePrChange;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.entity.Reviewer;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.domain.util.ReviewerChange;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.PullRequestJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.sheet.PullRequestSheetJson;
import org.sadtech.bot.bitbucketbot.service.ChangeService;
import org.sadtech.bot.bitbucketbot.service.PullRequestsService;
import org.sadtech.bot.bitbucketbot.service.UserService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.sadtech.bot.bitbucketbot.utils.NonNullUtils;
import org.sadtech.bot.bitbucketbot.utils.Pair;
import org.springframework.core.convert.ConversionService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.sadtech.bot.bitbucketbot.domain.PullRequestStatus.DECLINED;
import static org.sadtech.bot.bitbucketbot.domain.PullRequestStatus.DELETE;
import static org.sadtech.bot.bitbucketbot.domain.PullRequestStatus.MERGED;
import static org.sadtech.bot.bitbucketbot.domain.PullRequestStatus.OPEN;
/**
* @author upagge [30.01.2020]
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class SchedulerPullRequest {
private static final Set<PullRequestStatus> STATUSES = Stream.of(MERGED, OPEN, DECLINED).collect(Collectors.toSet());
private final PullRequestsService pullRequestsService;
private final UserService userService;
private final ChangeService changeService;
private final ConversionService conversionService;
private final BitbucketConfig bitbucketConfig;
@Scheduled(fixedRate = 30000)
public void checkPullRequest() {
final Set<Long> existsId = pullRequestsService.getAllId(STATUSES).stream()
.map(IdAndStatusPr::getId)
.collect(Collectors.toSet());
final Set<Long> openId = checkOpenPullRequest();
log.info("Открыты: " + Arrays.toString(openId.toArray()));
final Set<Long> closeId = checkClosePullRequest();
log.info("Закрыты: " + Arrays.toString(closeId.toArray()));
final Set<Long> newNotExistsId = existsId.stream()
.filter(id -> !openId.contains(id) && !closeId.contains(id))
.collect(Collectors.toSet());
log.info("Не найдены: " + Arrays.toString(newNotExistsId.toArray()));
if (!newNotExistsId.isEmpty()) {
updateDeletePr(newNotExistsId);
}
}
private void updateDeletePr(@NonNull Set<Long> ids) {
final Set<PullRequest> deletePr = pullRequestsService.getAllById(ids);
deletePr.stream()
.filter(pullRequest -> pullRequest.getAuthor().getTelegramId() != null)
.forEach(pullRequest -> changeService.add(
StatusPrChange.builder()
.name(pullRequest.getName())
.url(pullRequest.getUrl())
.oldStatus(pullRequest.getStatus())
.newStatus(DELETE)
.telegramIds(Collections.singleton(pullRequest.getAuthor().getTelegramId()))
.build()
));
pullRequestsService.updateAll(
deletePr.stream()
.peek(pullRequest -> pullRequest.setStatus(PullRequestStatus.DELETE))
.collect(Collectors.toList())
);
}
private Set<Long> checkClosePullRequest() {
final List<User> users = userService.getAllRegister();
final Set<Long> ids = new HashSet<>();
for (User user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(bitbucketConfig.getUrlPullRequestClose(), user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) {
final PullRequestSheetJson bitbucketSheet = sheetJson.get();
final Map<Long, PullRequest> existsPr = getExistsPr(bitbucketSheet.getValues());
final Set<PullRequest> pullRequests = pullRequestsService.getAllById(existsPr.keySet());
if (!existsPr.isEmpty() && !pullRequests.isEmpty()) {
processingUpdateClosePr(existsPr, pullRequests);
ids.addAll(
pullRequestsService.updateAll(existsPr.values()).stream()
.map(PullRequest::getId)
.collect(Collectors.toSet())
);
}
if (bitbucketSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(bitbucketConfig.getUrlPullRequestClose() + bitbucketSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class);
} else {
break;
}
}
}
return ids;
}
private Set<Long> checkOpenPullRequest() {
final List<User> users = userService.getAllRegister();
final Set<Long> ids = new HashSet<>();
for (User user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(bitbucketConfig.getUrlPullRequestOpen(), user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) {
final PullRequestSheetJson jsonSheet = sheetJson.get();
final Map<Long, PullRequest> existsPr = getExistsPr(jsonSheet.getValues());
final Set<PullRequest> pullRequests = pullRequestsService.getAllById(existsPr.keySet());
if (!existsPr.isEmpty() && !pullRequests.isEmpty()) {
processingUpdateOpenPr(existsPr, pullRequests);
ids.addAll(
pullRequestsService.updateAll(existsPr.values()).stream()
.map(PullRequest::getId)
.collect(Collectors.toSet())
);
}
if (jsonSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(bitbucketConfig.getUrlPullRequestOpen() + jsonSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class);
} else {
break;
}
}
}
return ids;
}
private Map<Long, PullRequest> getExistsPr(List<PullRequestJson> pullRequestJsons) {
return pullRequestJsons.stream()
.filter(Objects::nonNull)
.map(pullRequestJson -> conversionService.convert(pullRequestJson, PullRequest.class))
.peek(pullRequest -> pullRequestsService.getIdByBitbucketIdAndReposId(pullRequest.getBitbucketId(), pullRequest.getRepositoryId()).ifPresent(pullRequest::setId))
.filter(pullRequest -> pullRequest.getId() != null)
.collect(Collectors.toMap(PullRequest::getId, pullRequest -> pullRequest));
}
@NonNull
private void processingUpdateOpenPr(Map<Long, PullRequest> newPullRequests, Set<PullRequest> pullRequests) {
for (PullRequest pullRequest : pullRequests) {
PullRequest newPullRequest = newPullRequests.get(pullRequest.getId());
changeStatusPR(pullRequest, newPullRequest);
changeReviewersPR(pullRequest, newPullRequest);
processingReviewer(pullRequest, newPullRequest);
conflictPr(pullRequest, newPullRequest);
}
}
@NonNull
private void processingUpdateClosePr(Map<Long, PullRequest> newPullRequests, Set<PullRequest> pullRequests) {
for (PullRequest pullRequest : pullRequests) {
PullRequest newPullRequest = newPullRequests.get(pullRequest.getId());
changeStatusPR(pullRequest, newPullRequest);
}
}
@NonNull
private void conflictPr(PullRequest pullRequest, PullRequest newPullRequest) {
if (newPullRequest.isConflict() && !pullRequest.isConflict()) {
changeService.add(
ConflictPrChange.builder()
.name(pullRequest.getName())
.url(pullRequest.getUrl())
.telegramIds(NonNullUtils.telegramIdByUser(pullRequest.getAuthor()))
.build()
);
}
}
private void processingReviewer(PullRequest pullRequest, PullRequest newPullRequest) {
if (isUpdatePr(pullRequest, newPullRequest)) {
final Set<String> logins = newPullRequest.getReviewers().stream()
.map(Reviewer::getUser)
.collect(Collectors.toSet());
final Set<Long> telegramIds = userService.getAllTelegramIdByLogin(logins);
changeService.add(
UpdatePrChange.builder()
.name(newPullRequest.getName())
.url(newPullRequest.getUrl())
.author(newPullRequest.getAuthor().getLogin())
.telegramIds(telegramIds)
.build()
);
}
}
@NonNull
private boolean isUpdatePr(PullRequest pullRequest, PullRequest newPullRequest) {
LocalDateTime oldDate = pullRequest.getUpdateDate();
LocalDateTime newDate = newPullRequest.getUpdateDate();
return !oldDate.isEqual(newDate);
}
@NonNull
private void changeReviewersPR(PullRequest pullRequest, PullRequest newPullRequest) {
final Map<String, Reviewer> oldReviewers = pullRequest.getReviewers().stream()
.collect(Collectors.toMap(Reviewer::getUser, reviewer -> reviewer));
final Map<String, Reviewer> newReviewers = newPullRequest.getReviewers().stream()
.collect(Collectors.toMap(Reviewer::getUser, reviewer -> reviewer));
List<ReviewerChange> reviewerChanges = new ArrayList<>();
for (Reviewer newReviewer : newReviewers.values()) {
if (oldReviewers.containsKey(newReviewer.getUser())) {
final Reviewer oldReviewer = oldReviewers.get(newReviewer.getUser());
final ReviewerStatus oldStatus = oldReviewer.getStatus();
final ReviewerStatus newStatus = newReviewer.getStatus();
if (!oldStatus.equals(newStatus)) {
reviewerChanges.add(ReviewerChange.ofOld(oldReviewer.getUser(), oldStatus, newStatus));
}
} else {
reviewerChanges.add(ReviewerChange.ofNew(newReviewer.getUser(), newReviewer.getStatus()));
}
}
final Set<String> oldLogins = oldReviewers.keySet();
oldLogins.removeAll(newReviewers.keySet());
oldLogins.forEach(login -> reviewerChanges.add(ReviewerChange.ofDeleted(login)));
if (!reviewerChanges.isEmpty()) {
changeService.add(
ReviewersPrChange.builder()
.name(pullRequest.getName())
.url(pullRequest.getUrl())
.reviewerChanges(reviewerChanges)
.telegramIds(NonNullUtils.telegramIdByUser(pullRequest.getAuthor()))
.build()
);
}
}
@NonNull
private void changeStatusPR(PullRequest pullRequest, PullRequest newPullRequest) {
final PullRequestStatus oldStatus = pullRequest.getStatus();
final PullRequestStatus newStatus = newPullRequest.getStatus();
if (!oldStatus.equals(newStatus)) {
changeService.add(
StatusPrChange.builder()
.name(newPullRequest.getName())
.url(newPullRequest.getUrl())
.oldStatus(oldStatus)
.newStatus(newStatus)
.telegramIds(NonNullUtils.telegramIdByUser(newPullRequest.getAuthor()))
.build()
);
}
}
@Scheduled(fixedRate = 30000)
public void checkNewPullRequest() {
final List<User> users = userService.getAllRegister();
for (User user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(bitbucketConfig.getUrlPullRequestOpen(), user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) {
final PullRequestSheetJson pullRequestBitbucketSheet = sheetJson.get();
final List<PullRequest> newPullRequest = pullRequestBitbucketSheet.getValues().stream()
.collect(Collectors.toMap(pullRequestJson -> new Pair<>(pullRequestJson.getId(), pullRequestJson.getFromRef().getRepository().getId()), pullRequestJson -> pullRequestJson))
.entrySet()
.stream()
.filter(test -> !pullRequestsService.existsByBitbucketIdAndReposId(test.getKey().getKey(), test.getKey().getValue()))
.map(test -> conversionService.convert(test.getValue(), PullRequest.class))
.collect(Collectors.toList());
final List<PullRequest> newPullRequests = pullRequestsService.addAll(newPullRequest);
sendNotificationNewPullRequest(newPullRequests);
if (pullRequestBitbucketSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(bitbucketConfig.getUrlPullRequestOpen() + pullRequestBitbucketSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class);
} else {
break;
}
}
}
}
private void sendNotificationNewPullRequest(@NonNull List<PullRequest> newPullRequests) {
if (!newPullRequests.isEmpty()) {
for (PullRequest newPullRequest : newPullRequests) {
final Set<Long> reviewerTelegramIds = userService.getAllTelegramIdByLogin(newPullRequest.getReviewers().stream()
.map(Reviewer::getUser)
.collect(Collectors.toSet()));
changeService.add(
NewPrChange.builder()
.name(newPullRequest.getName())
.url(newPullRequest.getUrl())
.description(newPullRequest.getDescription())
.author(newPullRequest.getAuthor().getLogin())
.telegramIds(reviewerTelegramIds)
.build()
);
}
}
}
}

View File

@ -60,7 +60,9 @@ public class SchedulerPushMessageSend {
List<MessageSend> pushMessage = messageSendService.getPushMessage();
if (!pushMessage.isEmpty()) {
try {
sendMessage(objectMapper.writeValueAsString(pushMessage));
final String json = objectMapper.writeValueAsString(pushMessage);
// sendMessage(json);
System.out.println(json);
} catch (JsonProcessingException e) {
log.error(e.getMessage());
}

View File

@ -0,0 +1,24 @@
package org.sadtech.bot.bitbucketbot.scheduler.parser;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.service.parser.CommentAndTaskParser;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class CommentAndTaskScheduler {
private final CommentAndTaskParser commentAndTaskParser;
@Scheduled(cron = "")
public void scanNewCommentAndTask() {
commentAndTaskParser.scanNewCommentAndTask();
}
@Scheduled(cron = "")
public void scanOldComment() {
commentAndTaskParser.scanOldComment();
}
}

View File

@ -0,0 +1,20 @@
package org.sadtech.bot.bitbucketbot.scheduler.parser;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.service.parser.PersonParser;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class PersonScheduler {
private final PersonParser personParser;
@Scheduled(cron = "")
public void scanPersons() {
personParser.scanNewPerson();
}
}

View File

@ -0,0 +1,26 @@
package org.sadtech.bot.bitbucketbot.scheduler.parser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sadtech.bot.bitbucketbot.service.parser.PullRequestParser;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class PullRequestParserScheduler {
private final PullRequestParser pullRequestParser;
@Scheduled(cron = "")
public void parsingOldPullRequest() {
pullRequestParser.parsingOldPullRequest();
}
@Scheduled(cron = "")
public void parsingNewPullRequest() {
pullRequestParser.parsingNewPullRequest();
}
}

View File

@ -5,10 +5,24 @@ import org.sadtech.bot.bitbucketbot.domain.change.Change;
import java.util.List;
/**
* Сервис по работе с изменениями в битбакете.
*
* @author upagge
* @see Change
*/
public interface ChangeService {
/**
* Позволяет добавить новое изменение в хранилище
*
* @param change Объект, который содержит изменения
*/
void add(@NonNull Change change);
/**
* Позволяет получить новые изменения.
*/
List<Change> getNew();
}

View File

@ -7,6 +7,8 @@ import org.springframework.data.domain.Page;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface CommentService {
@ -17,8 +19,15 @@ public interface CommentService {
@NonNull
List<Comment> getAllBetweenDate(LocalDateTime dateFrom, LocalDateTime dateTo);
void save(@NonNull Comment comment);
Comment create(@NonNull Comment comment);
void delete(@NonNull Long id);
Optional<Comment> getProxyById(@NonNull Long id);
List<Comment> createAll(List<Comment> newComments);
Comment update(Comment comment);
List<Comment> getAllById(@NonNull Set<Long> ids);
}

View File

@ -0,0 +1,33 @@
package org.sadtech.bot.bitbucketbot.service;
import lombok.NonNull;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface PersonService {
Optional<Person> getByLogin(String login);
Set<String> existsByLogin(@NonNull Set<String> logins);
boolean existsByLogin(@NonNull String login);
Person reg(@NonNull Person user);
List<Person> getAllRegister();
Optional<Long> getTelegramIdByLogin(@NonNull String login);
Set<Long> getAllTelegramIdByLogin(Set<String> logins);
Optional<Person> getProxyByLogin(@NonNull String login);
Person create(@NonNull Person person);
List<Person> createAll(Collection<Person> newUsers);
}

View File

@ -1,46 +1,24 @@
package org.sadtech.bot.bitbucketbot.service;
import lombok.NonNull;
import org.sadtech.basic.context.service.BusinessLogicService;
import org.sadtech.basic.context.service.simple.FilterService;
import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr;
import org.sadtech.bot.bitbucketbot.domain.Pagination;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.springframework.data.domain.Page;
import org.sadtech.bot.bitbucketbot.domain.filter.PullRequestFilter;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface PullRequestsService {
@NonNull
boolean existsByBitbucketIdAndReposId(Long bitbucketId, Long repositoryId);
Set<PullRequest> getAllById(@NonNull Set<Long> pullRequestJsonId);
List<PullRequest> addAll(@NonNull Collection<PullRequest> pullRequests);
List<PullRequest> updateAll(@NonNull Collection<PullRequest> pullRequests);
@NonNull
Optional<Long> getIdByBitbucketIdAndReposId(Long bitbucketId, Long repositoryId);
void deleteAll(@NonNull Set<Long> id);
public interface PullRequestsService extends BusinessLogicService<PullRequest, Long>, FilterService<PullRequest, PullRequestFilter> {
@NonNull
List<PullRequest> getAllByReviewerAndStatuses(@NonNull String login, @NonNull ReviewerStatus reviewerStatus, @NonNull Set<PullRequestStatus> pullRequestStatuses);
List<PullRequest> getAllByAuthorAndReviewerStatus(@NonNull String login, @NonNull ReviewerStatus status);
Set<Long> getAllId();
Set<IdAndStatusPr> getAllId(Set<PullRequestStatus> statuses);
Page<PullRequest> getAll(@NonNull Pagination pagination);
List<PullRequest> getAllByAuthor(@NonNull String login, @NonNull LocalDateTime dateFrom, @NonNull LocalDateTime dateTo);
}

View File

@ -0,0 +1,21 @@
package org.sadtech.bot.bitbucketbot.service;
import lombok.NonNull;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import java.util.Collection;
import java.util.List;
public interface TaskService {
Task create(@NonNull Task task);
void deleteById(@NonNull Long id);
Task update(@NonNull Task task);
List<Task> createAll(@NonNull Collection<Task> tasks);
Long getLastTaskId();
}

View File

@ -1,26 +0,0 @@
package org.sadtech.bot.bitbucketbot.service;
import lombok.NonNull;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public interface UserService {
Optional<User> getByLogin(String login);
Set<String> existsByLogin(@NonNull Set<String> logins);
User reg(@NonNull User user);
List<User> addAll(Set<User> newUsers);
List<User> getAllRegister();
Optional<Long> getTelegramIdByLogin(@NonNull String login);
Set<Long> getAllTelegramIdByLogin(Set<String> logins);
}

View File

@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class Utils {
private static ObjectMapper objectMapper;
private static final ObjectMapper objectMapper;
static {
objectMapper = new ObjectMapper();
@ -35,8 +35,7 @@ public class Utils {
throw new IllegalStateException("Утилитарный класс");
}
@NonNull
public static <T> Optional<T> urlToJson(String urlValue, String token, Class<T> classOfT) {
public static <T> Optional<T> urlToJson(@NonNull String urlValue, @NonNull String token, @NonNull Class<T> classOfT) {
Request request = new Request.Builder()
.url(urlValue)
.header("Authorization", "Bearer " + token)

View File

@ -5,15 +5,12 @@ import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.entity.Reviewer;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.Outcome;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.Properties;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.PullRequestJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.PullRequestState;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.UserDecisionJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.UserJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.UserPullRequestStatus;
import org.sadtech.bot.bitbucketbot.service.UserService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@ -24,27 +21,25 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class PullRequestJsonConverter implements Converter<PullRequestJson, PullRequest> {
private final UserJsonConverter userJsonConverter;
private final UserService userService;
@Override
public PullRequest convert(PullRequestJson json) {
return PullRequest.builder()
.bitbucketId(json.getId())
.version(json.getVersion())
.createDate(json.getCreatedDate())
.updateDate(json.getUpdatedDate())
.conflict(convertConflict(json.getProperties()))
.description(convertDescription(json.getDescription()))
.repositoryId(json.getFromRef().getRepository().getId())
.author(this.convertUser(json.getAuthor().getUser()))
.name(json.getTitle())
.url(json.getLinks().getSelf().get(0).getHref())
.status(convertPullRequestStatus(json.getState()))
.projectKey(json.getFromRef().getRepository().getProject().getKey())
.repositorySlug(json.getFromRef().getRepository().getSlug())
.reviewers(convertReviewers(json.getReviewers()))
.build();
final PullRequest pullRequest = new PullRequest();
pullRequest.setBitbucketId(json.getId());
pullRequest.setCreateDate(json.getCreatedDate());
pullRequest.setUpdateDate(json.getUpdatedDate());
pullRequest.setConflict(convertConflict(json.getProperties()));
pullRequest.setDescription(convertDescription(json.getDescription()));
pullRequest.setAuthorLogin(json.getAuthor().getUser().getName());
pullRequest.setTitle(json.getTitle());
pullRequest.setUrl(json.getLinks().getSelf().get(0).getHref());
pullRequest.setStatus(convertPullRequestStatus(json.getState()));
pullRequest.setProjectKey(json.getFromRef().getRepository().getProject().getKey());
pullRequest.setRepositorySlug(json.getFromRef().getRepository().getSlug());
pullRequest.setReviewers(convertReviewers(json.getReviewers()));
pullRequest.setBitbucketVersion(json.getVersion());
return pullRequest;
}
private boolean convertConflict(Properties properties) {
@ -61,10 +56,6 @@ public class PullRequestJsonConverter implements Converter<PullRequestJson, Pull
return null;
}
private User convertUser(UserJson userJson) {
return userService.getByLogin(userJson.getName()).orElse(userJsonConverter.convert(userJson));
}
public static PullRequestStatus convertPullRequestStatus(PullRequestState state) {
switch (state) {
case OPEN:
@ -82,7 +73,7 @@ public class PullRequestJsonConverter implements Converter<PullRequestJson, Pull
.map(
jsonReviewer -> {
final Reviewer reviewer = new Reviewer();
reviewer.setUser(jsonReviewer.getUser().getName());
reviewer.setUserLogin(jsonReviewer.getUser().getName());
reviewer.setStatus(convertStatusReviewer(jsonReviewer.getStatus()));
return reviewer;
}

View File

@ -0,0 +1,49 @@
package org.sadtech.bot.bitbucketbot.service.converter;
import org.sadtech.bot.bitbucketbot.domain.entity.Comment;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.Severity;
import org.sadtech.bot.bitbucketbot.service.executor.ResultScan;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.util.stream.Collectors;
@Component
public class ResultScanToComment implements Converter<ResultScan, Comment> {
@Override
public Comment convert(ResultScan resultScan) {
final CommentJson commentJson = resultScan.getCommentJson();
final Comment comment = new Comment();
comment.setCreateDate(commentJson.getCreatedDate());
comment.setAuthor(commentJson.getAuthor().getName());
comment.setPullRequestId(getPullRequest(resultScan.getPullRequestId()));
comment.setMessage(commentJson.getText());
comment.setUrl(resultScan.getUrlComment());
comment.setBitbucketVersion(commentJson.getVersion());
comment.setAnswers(
commentJson.getComments().stream()
.filter(json -> Severity.NORMAL.equals(json.getSeverity()))
.map(CommentJson::getId)
.collect(Collectors.toSet())
);
return comment;
}
private PullRequest getPullRequest(Long pullRequestId) {
final PullRequest pullRequest = new PullRequest();
pullRequest.setId(pullRequestId);
return pullRequest;
}
private Person getAuthor(String name) {
final Person user = new Person();
user.setLogin(name);
return user;
}
}

View File

@ -0,0 +1,32 @@
package org.sadtech.bot.bitbucketbot.service.converter;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentJson;
import org.sadtech.bot.bitbucketbot.service.executor.ResultScan;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
public class ResultScanToTaskConvert implements Converter<ResultScan, Task> {
@Override
public Task convert(ResultScan resultScan) {
final CommentJson json = resultScan.getCommentJson();
final Task task = new Task();
task.setId(json.getId());
task.setAuthor(getAuthor(json));
task.setDescription(json.getText());
task.setCreateDate(json.getCreatedDate());
task.setBitbucketVersion(json.getVersion());
task.setPullRequestId(resultScan.getPullRequestId());
return task;
}
private Person getAuthor(CommentJson json) {
final Person person = new Person();
person.setLogin(json.getAuthor().getName());
return person;
}
}

View File

@ -1,16 +1,16 @@
package org.sadtech.bot.bitbucketbot.service.converter;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.dto.UserDto;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
public class UserDtoConverter implements Converter<UserDto, User> {
public class UserDtoConverter implements Converter<UserDto, Person> {
@Override
public User convert(UserDto source) {
return User.builder()
public Person convert(UserDto source) {
return Person.builder()
.login(source.getLogin())
.token(source.getToken())
.telegramId(source.getTelegramId())

View File

@ -1,19 +1,19 @@
package org.sadtech.bot.bitbucketbot.service.converter;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.UserJson;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Service;
@Service
public class UserJsonConverter implements Converter<UserJson, User> {
public class UserJsonConverter implements Converter<UserJson, Person> {
@Override
public User convert(UserJson source) {
return User.builder()
.fullName(source.getDisplayName())
.login(source.getName())
.build();
public Person convert(UserJson source) {
final Person person = new Person();
person.setFullName(source.getDisplayName());
person.setLogin(source.getName());
return person;
}
}

View File

@ -6,6 +6,6 @@ import lombok.Data;
public class DataScan {
private final String urlComment;
private final String urlPr;
private final Long pullRequestId;
}

View File

@ -1,13 +1,15 @@
package org.sadtech.bot.bitbucketbot.service.executor;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentJson;
@Data
@Getter
@RequiredArgsConstructor
public class ResultScan {
private final String urlComment;
private final String urlPr;
private final Long pullRequestId;
private final CommentJson commentJson;
}

View File

@ -16,7 +16,12 @@ public class Seeker implements Callable<Optional<ResultScan>> {
@Override
public Optional<ResultScan> call() {
return Utils.urlToJson(dataScan.getUrlComment(), token, CommentJson.class)
.map(commentJson -> new ResultScan(dataScan.getUrlComment(), dataScan.getUrlPr(), commentJson));
.map(
commentJson -> new ResultScan(
dataScan.getUrlComment(),
dataScan.getPullRequestId(),
commentJson)
);
}
}

View File

@ -5,20 +5,29 @@ import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.config.InitConfig;
import org.sadtech.bot.bitbucketbot.domain.Pagination;
import org.sadtech.bot.bitbucketbot.domain.entity.Comment;
import org.sadtech.bot.bitbucketbot.exception.NotFoundException;
import org.sadtech.bot.bitbucketbot.repository.jpa.CommentRepository;
import org.sadtech.bot.bitbucketbot.service.CommentService;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.sadtech.bot.bitbucketbot.service.PullRequestsService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class CommentServiceImpl implements CommentService {
private final PersonService personService;
private final CommentRepository commentRepository;
private final PullRequestsService pullRequestsService;
private final InitConfig initConfig;
@Override
@ -41,8 +50,14 @@ public class CommentServiceImpl implements CommentService {
}
@Override
public void save(@NonNull Comment comment) {
commentRepository.save(comment);
public Comment create(@NonNull Comment comment) {
comment.setAuthor(personService.getProxyByLogin(
comment.getAuthor().getLogin()).orElseThrow(() -> new NotFoundException(""))
);
comment.setPullRequest(
pullRequestsService.getProxyById(comment.getPullRequest().getId()).orElseThrow(() -> new NotFoundException(""))
);
return commentRepository.save(comment);
}
@Override
@ -50,4 +65,36 @@ public class CommentServiceImpl implements CommentService {
commentRepository.deleteById(id);
}
@Override
public Optional<Comment> getProxyById(@NonNull Long id) {
return Optional.ofNullable(commentRepository.getOne(id));
}
@Override
@Transactional
public List<Comment> createAll(List<Comment> newComments) {
return newComments.stream()
.map(this::create)
.collect(Collectors.toList());
}
@Override
public Comment update(Comment comment) {
final Comment oldComment = commentRepository.findById(comment.getId())
.orElseThrow(() -> new NotFoundException("Комментарий не найден"));
if (oldComment.getBitbucketVersion().equals(comment.getBitbucketVersion())) {
oldComment.setBitbucketVersion(comment.getBitbucketVersion());
oldComment.setMessage(oldComment.getMessage());
return commentRepository.save(oldComment);
}
return oldComment;
}
@Override
public List<Comment> getAllById(@NonNull Set<Long> ids) {
return commentRepository.findAllById(ids);
}
}

View File

@ -3,7 +3,7 @@ package org.sadtech.bot.bitbucketbot.service.impl;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sadtech.bot.bitbucketbot.config.BitbucketConfig;
import org.sadtech.bot.bitbucketbot.config.properties.BitbucketProperty;
import org.sadtech.bot.bitbucketbot.service.executor.DataScan;
import org.sadtech.bot.bitbucketbot.service.executor.Executor;
import org.sadtech.bot.bitbucketbot.service.executor.ResultScan;
@ -25,13 +25,13 @@ public class ExecutorScanner implements Executor<DataScan, ResultScan> {
private final ExecutorService executorService;
private List<Future<Optional<ResultScan>>> resultList = new ArrayList<>();
private final BitbucketConfig bitbucketConfig;
private final BitbucketProperty bitbucketProperty;
@Override
public boolean registration(@NonNull List<DataScan> dataScans) {
resultList.addAll(
dataScans.stream()
.map(dataScan -> new Seeker(dataScan, bitbucketConfig.getToken()))
.map(dataScan -> new Seeker(dataScan, bitbucketProperty.getToken()))
.map(executorService::submit)
.collect(Collectors.toList())
);

View File

@ -0,0 +1,96 @@
package org.sadtech.bot.bitbucketbot.service.impl;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.config.properties.BitbucketProperty;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.sheet.PullRequestSheetJson;
import org.sadtech.bot.bitbucketbot.exception.CreateException;
import org.sadtech.bot.bitbucketbot.exception.RegException;
import org.sadtech.bot.bitbucketbot.repository.jpa.PersonRepository;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PersonServiceImpl implements PersonService {
private final PersonRepository personRepository;
private final BitbucketProperty bitbucketProperty;
@Override
public Optional<Person> getByLogin(String login) {
return personRepository.findById(login);
}
@Override
public Set<String> existsByLogin(@NonNull Set<String> logins) {
return logins.stream().filter(personRepository::existsById).collect(Collectors.toSet());
}
@Override
public boolean existsByLogin(@NonNull String login) {
return personRepository.existsByLogin(login);
}
@Override
public Person reg(@NonNull Person user) {
final Optional<Person> optUser = personRepository.findByLogin(user.getLogin());
if (optUser.isPresent()) {
final Person oldUser = optUser.get();
if (oldUser.getTelegramId() == null) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(bitbucketProperty.getUrlPullRequestClose(), user.getToken(), PullRequestSheetJson.class);
if (sheetJson.isPresent()) {
oldUser.setTelegramId(user.getTelegramId());
return personRepository.save(oldUser);
} else {
throw new RegException("Ваш токен не валиден");
}
} else {
throw new RegException("Вы уже авторизованы в системе");
}
}
throw new RegException("Пользователь не найден, подождите обновление базы пользователей!");
}
@Override
public List<Person> getAllRegister() {
return personRepository.findAllByTelegramIdNotNullAndTokenNotNull();
}
@Override
public Optional<Long> getTelegramIdByLogin(@NonNull String login) {
return Optional.ofNullable(personRepository.findTelegramIdByLogin(login));
}
@Override
public Set<Long> getAllTelegramIdByLogin(Set<String> logins) {
return personRepository.findAllTelegramIdByLogin(logins);
}
@Override
public Optional<Person> getProxyByLogin(@NonNull String login) {
return Optional.ofNullable(personRepository.getByLogin(login));
}
@Override
public Person create(@NonNull Person person) {
if (person.getId() == null) {
return personRepository.save(person);
}
throw new CreateException("При создании пользователя должен отсутствовать id");
}
@Override
public List<Person> createAll(Collection<Person> newPersons) {
return newPersons.stream().map(this::create).collect(Collectors.toList());
}
}

View File

@ -1,65 +1,78 @@
package org.sadtech.bot.bitbucketbot.service.impl;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.basic.context.page.Pagination;
import org.sadtech.basic.context.page.Sheet;
import org.sadtech.basic.core.service.AbstractBusinessLogicService;
import org.sadtech.basic.filter.criteria.CriteriaQuery;
import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr;
import org.sadtech.bot.bitbucketbot.domain.Pagination;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.repository.jpa.PullRequestsRepository;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest_;
import org.sadtech.bot.bitbucketbot.domain.filter.PullRequestFilter;
import org.sadtech.bot.bitbucketbot.exception.CreateException;
import org.sadtech.bot.bitbucketbot.exception.UpdateException;
import org.sadtech.bot.bitbucketbot.repository.PullRequestsRepository;
import org.sadtech.bot.bitbucketbot.service.ChangeService;
import org.sadtech.bot.bitbucketbot.service.PullRequestsService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.sadtech.bot.bitbucketbot.utils.ChangeGenerator;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PullRequestsServiceImpl implements PullRequestsService {
public class PullRequestsServiceImpl extends AbstractBusinessLogicService<PullRequest, Long> implements PullRequestsService {
private final ChangeService changeService;
private final PullRequestsRepository pullRequestsRepository;
@Override
public boolean existsByBitbucketIdAndReposId(@NonNull Long bitbucketId, @NonNull Long repositoryId) {
return pullRequestsRepository.existsByBitbucketIdAndRepositoryId(bitbucketId, repositoryId);
protected PullRequestsServiceImpl(PullRequestsRepository pullRequestsRepository, ChangeService changeService) {
super(pullRequestsRepository);
this.changeService = changeService;
this.pullRequestsRepository = pullRequestsRepository;
}
@Override
public Set<PullRequest> getAllById(@NonNull Set<Long> pullRequestJsonId) {
return pullRequestsRepository.findAllByIdIn(pullRequestJsonId);
public PullRequest create(@NonNull PullRequest pullRequest) {
if (pullRequest.getId() == null) {
final PullRequest newPullRequest = pullRequestsRepository.save(pullRequest);
changeService.add(ChangeGenerator.create(newPullRequest));
return newPullRequest;
}
throw new CreateException("При создании идентификатор должен быть пустым");
}
@Override
@Transactional
public List<PullRequest> addAll(@NonNull Collection<PullRequest> pullRequests) {
return pullRequestsRepository.saveAll(pullRequests);
public PullRequest update(@NonNull PullRequest pullRequest) {
final PullRequest oldPullRequest = findAndFillId(pullRequest);
if (!oldPullRequest.getBitbucketVersion().equals(pullRequest.getBitbucketVersion())) {
oldPullRequest.setBitbucketVersion(pullRequest.getVersion());
oldPullRequest.setConflict(pullRequest.isConflict());
oldPullRequest.setTitle(pullRequest.getTitle());
oldPullRequest.setDescription(pullRequest.getDescription());
oldPullRequest.setStatus(pullRequest.getStatus());
oldPullRequest.setReviewers(pullRequest.getReviewers());
final PullRequest newPullRequest = pullRequestsRepository.save(oldPullRequest);
changeService.add(ChangeGenerator.createUpdatePr(pullRequest, newPullRequest));
changeService.add(ChangeGenerator.createReviewersPr(pullRequest, newPullRequest));
return newPullRequest;
}
return oldPullRequest;
}
@Override
public List<PullRequest> updateAll(@NonNull Collection<PullRequest> pullRequests) {
final List<PullRequest> updatePullRequests = pullRequests.stream()
.filter(pullRequest -> pullRequestsRepository.existsById(pullRequest.getId()))
.collect(Collectors.toList());
return pullRequestsRepository.saveAll(updatePullRequests);
}
@Override
public Optional<Long> getIdByBitbucketIdAndReposId(@NonNull Long bitbucketId, @NonNull Long repositoryId) {
return pullRequestsRepository.findIdByBitbucketIdAndRepositoryId(bitbucketId, repositoryId);
}
@Override
@Transactional
public void deleteAll(@NonNull Set<Long> id) {
pullRequestsRepository.deleteAllByIdIn(id);
private PullRequest findAndFillId(@NonNull PullRequest pullRequest) {
return pullRequestsRepository.findByFilterQuery(
CriteriaQuery.create()
.matchPhrase(PullRequest_.BITBUCKET_ID, pullRequest.getBitbucketId())
.matchPhrase(PullRequest_.REPOSITORY_ID, pullRequest.getRepositoryId())
).orElseThrow(() -> new UpdateException("ПР с таким id не существует"));
}
@NonNull
@ -73,25 +86,33 @@ public class PullRequestsServiceImpl implements PullRequestsService {
return pullRequestsRepository.findAllByAuthorAndReviewerStatus(login, status);
}
@Override
public Set<Long> getAllId() {
return pullRequestsRepository.findAllIds();
}
@Override
public Set<IdAndStatusPr> getAllId(Set<PullRequestStatus> statuses) {
return pullRequestsRepository.findAllIdByStatusIn(statuses);
}
@Override
public Page<PullRequest> getAll(@NonNull Pagination pagination) {
return pullRequestsRepository.findAll(PageRequest.of(pagination.getPage(), pagination.getSize()));
public Sheet<PullRequest> getAllByFilter(@NonNull PullRequestFilter filter, Pagination pagination) {
return null;
}
@Override
public List<PullRequest> getAllByAuthor(@NonNull String login, @NonNull LocalDateTime dateFrom, @NonNull LocalDateTime dateTo) {
return pullRequestsRepository.findAllByAuthorAndDateBetween(login, dateFrom, dateTo);
public Sheet<PullRequest> getALlByFilterQuery(@NonNull PullRequestFilter filter, Pagination pagination) {
return null;
}
@Override
public Optional<PullRequest> getByFilterQuery(@NonNull PullRequestFilter filterQuery) {
return Optional.empty();
}
@Override
public boolean existsByFilterQuery(@NonNull PullRequestFilter filter) {
return pullRequestsRepository.existsByFilterQuery(
CriteriaQuery.<PullRequest>create()
.matchPhrase(PullRequest_.BITBUCKET_ID, filter.getBitbucketId())
.matchPhrase(PullRequest_.REPOSITORY_ID, filter.getBitbucketRepositoryId())
);
}
}

View File

@ -0,0 +1,52 @@
package org.sadtech.bot.bitbucketbot.service.impl;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import org.sadtech.bot.bitbucketbot.exception.CreateException;
import org.sadtech.bot.bitbucketbot.exception.NotFoundException;
import org.sadtech.bot.bitbucketbot.repository.TaskRepository;
import org.sadtech.bot.bitbucketbot.service.TaskService;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class TaskServiceImpl implements TaskService {
private final TaskRepository taskRepository;
@Override
public Task create(@NonNull Task task) {
if (task.getId() == null) {
return taskRepository.save(task);
}
throw new CreateException("При создании объекта не должно быть идентификатора");
}
@Override
public void deleteById(@NonNull Long id) {
taskRepository.deleteById(id);
}
@Override
public Task update(@NonNull Task task) {
final Task oldTask = taskRepository.findById(task.getId()).orElseThrow(() -> new NotFoundException("Задача не найдена"));
oldTask.setStatus(task.getStatus());
return taskRepository.save(oldTask);
}
@Override
public List<Task> createAll(@NonNull Collection<Task> tasks) {
return tasks.stream().map(this::create).collect(Collectors.toList());
}
@Override
public Long getLastTaskId() {
return taskRepository.findFirstByOrderByIdDesc().map(Task::getId).orElse(0L);
}
}

View File

@ -1,76 +0,0 @@
package org.sadtech.bot.bitbucketbot.service.impl;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.config.BitbucketConfig;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.sheet.PullRequestSheetJson;
import org.sadtech.bot.bitbucketbot.exception.RegException;
import org.sadtech.bot.bitbucketbot.repository.jpa.UserRepository;
import org.sadtech.bot.bitbucketbot.service.UserService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final BitbucketConfig bitbucketConfig;
@Override
public Optional<User> getByLogin(String login) {
return userRepository.findById(login);
}
@Override
public Set<String> existsByLogin(@NonNull Set<String> logins) {
return logins.stream().filter(userRepository::existsById).collect(Collectors.toSet());
}
@Override
public User reg(@NonNull User user) {
final Optional<User> optUser = userRepository.findByLogin(user.getLogin());
if (optUser.isPresent()) {
final User oldUser = optUser.get();
if (oldUser.getTelegramId() == null) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(bitbucketConfig.getUrlPullRequestClose(), user.getToken(), PullRequestSheetJson.class);
if (sheetJson.isPresent()) {
oldUser.setTelegramId(user.getTelegramId());
return userRepository.save(oldUser);
} else {
throw new RegException("Ваш токен не валиден");
}
} else {
throw new RegException("Вы уже авторизованы в системе");
}
}
throw new RegException("Пользователь не найден, подождите обновление базы пользователей!");
}
@Override
public List<User> addAll(Set<User> newUsers) {
return userRepository.saveAll(newUsers);
}
@Override
public List<User> getAllRegister() {
return userRepository.findAllByTelegramIdNotNullAndTokenNotNull();
}
@Override
public Optional<Long> getTelegramIdByLogin(@NonNull String login) {
return Optional.ofNullable(userRepository.findTelegramIdByLogin(login));
}
@Override
public Set<Long> getAllTelegramIdByLogin(Set<String> logins) {
return userRepository.findAllTelegramIdByLogin(logins);
}
}

View File

@ -0,0 +1,213 @@
package org.sadtech.bot.bitbucketbot.service.parser;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.config.properties.BitbucketProperty;
import org.sadtech.bot.bitbucketbot.config.properties.CommentSchedulerProperty;
import org.sadtech.bot.bitbucketbot.domain.Answer;
import org.sadtech.bot.bitbucketbot.domain.Pagination;
import org.sadtech.bot.bitbucketbot.domain.change.ChangeType;
import org.sadtech.bot.bitbucketbot.domain.change.comment.AnswerCommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.comment.CommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.task.TaskChange;
import org.sadtech.bot.bitbucketbot.domain.entity.Comment;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.entity.Task;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.Severity;
import org.sadtech.bot.bitbucketbot.service.ChangeService;
import org.sadtech.bot.bitbucketbot.service.CommentService;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.sadtech.bot.bitbucketbot.service.PullRequestsService;
import org.sadtech.bot.bitbucketbot.service.TaskService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.sadtech.bot.bitbucketbot.service.executor.DataScan;
import org.sadtech.bot.bitbucketbot.service.executor.ResultScan;
import org.sadtech.bot.bitbucketbot.service.impl.ExecutorScanner;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* <p>Поиск новых комментариев и задач.</p>
* <p>К несчастью, у битбакета не очень удобный API, и у них таска это то же самое что и комментарий, только с флагом</p>
*/
@Component
@RequiredArgsConstructor
public class CommentAndTaskParser {
private static final Pattern PATTERN = Pattern.compile("@[\\w]+");
private final CommentService commentService;
private final PullRequestsService pullRequestsService;
private final PersonService personService;
private final ChangeService changeService;
private final ExecutorScanner executorScanner;
private final TaskService taskService;
private final ConversionService conversionService;
private final BitbucketProperty bitbucketProperty;
private final CommentSchedulerProperty commentSchedulerProperty;
public void scanNewCommentAndTask() {
long commentId = getLastIdCommentOrTask() + 1;
int count = 0;
do {
final List<DataScan> dataScans = generatingLinksToPossibleComments(commentId);
executorScanner.registration(dataScans);
final List<ResultScan> resultScans = executorScanner.getResult();
if (!resultScans.isEmpty()) {
processingComments(resultScans);
processingTasks(resultScans);
count = 0;
}
} while (count++ < commentSchedulerProperty.getNoCommentCount());
}
private long getLastIdCommentOrTask() {
return Long.max(commentService.getLastCommentId(), taskService.getLastTaskId());
}
private void processingComments(List<ResultScan> resultScans) {
final List<Comment> newComments = commentService.createAll(getCommentsByResultScan(resultScans));
newComments.forEach(this::notificationPersonal);
}
private void processingTasks(List<ResultScan> resultScans) {
final List<Task> newTasks = taskService.createAll(getTaskByResultScan(resultScans));
newTasks.forEach(this::notificationNewTask);
}
private List<DataScan> generatingLinksToPossibleComments(@NonNull Long commentId) {
List<DataScan> commentUrls = new ArrayList<>();
for (int i = 0; i < 5; i++) {
int page = 0;
Page<PullRequest> pullRequestPage = pullRequestsService.getAll(
Pagination.of(page, commentSchedulerProperty.getCommentCount())
);
while (pullRequestPage.hasContent()) {
long finalCommentId = commentId;
commentUrls.addAll(pullRequestPage.getContent().stream()
.map(
pullRequest -> new DataScan(
getCommentUrl(finalCommentId, pullRequest),
pullRequest.getId()
)
)
.collect(Collectors.toList()));
pullRequestPage = pullRequestsService.getAll(
Pagination.of(++page, commentSchedulerProperty.getCommentCount())
);
}
commentId++;
}
return commentUrls;
}
private List<Comment> getCommentsByResultScan(List<ResultScan> resultScans) {
return resultScans.stream()
.filter(resultScan -> Severity.NORMAL.equals(resultScan.getCommentJson().getSeverity()))
.map(resultScan -> conversionService.convert(resultScan, Comment.class))
.collect(Collectors.toList());
}
private List<Task> getTaskByResultScan(List<ResultScan> resultScans) {
return resultScans.stream()
.filter(commentJson -> Severity.BLOCKER.equals(commentJson.getCommentJson().getSeverity()))
.map(resultScan -> conversionService.convert(resultScan, Task.class))
.collect(Collectors.toList());
}
private String getCommentUrl(long commentId, PullRequest pullRequest) {
return bitbucketProperty.getUrlPullRequestComment()
.replace("{projectKey}", pullRequest.getProjectKey())
.replace("{repositorySlug}", pullRequest.getRepositorySlug())
.replace("{pullRequestId}", pullRequest.getBitbucketId().toString())
.replace("{commentId}", String.valueOf(commentId));
}
private void notificationPersonal(@NonNull Comment comment) {
Matcher matcher = PATTERN.matcher(comment.getMessage());
Set<String> recipientsLogins = new HashSet<>();
while (matcher.find()) {
final String login = matcher.group(0).replace("@", "");
recipientsLogins.add(login);
}
final Set<Long> recipientsIds = personService.getAllTelegramIdByLogin(recipientsLogins);
changeService.add(
CommentChange.builder()
.authorName(comment.getAuthor().getLogin())
.url(comment.getUrl())
.telegramIds(recipientsIds)
.message(comment.getMessage())
.build()
);
}
private void notificationNewTask(@NonNull Task task) {
changeService.add(
TaskChange.builder()
.authorName(task.getAuthor().getFullName())
.messageTask(task.getDescription())
.type(ChangeType.NEW_TASK)
.url(task.getUrl())
.telegramIds(Collections.singleton(task.getPullRequest().getAuthor().getTelegramId()))
.build()
);
}
public void scanOldComment() {
@NonNull final List<Comment> comments = commentService.getAllBetweenDate(
LocalDateTime.now().minusDays(10), LocalDateTime.now()
);
for (Comment oldComment : comments) {
final Optional<CommentJson> optCommentJson = Utils.urlToJson(
oldComment.getUrl(),
bitbucketProperty.getToken(),
CommentJson.class
);
final Comment newComment = commentService.update(conversionService.convert(oldComment, Comment.class));
if (optCommentJson.isPresent()) {
final CommentJson commentJson = optCommentJson.get();
notifyNewCommentAnswers(oldComment, newComment);
}
}
}
private void notifyNewCommentAnswers(Comment oldComment, Comment newComment) {
final Set<Long> oldAnswerIds = oldComment.getAnswers();
final Set<Long> newAnswerIds = newComment.getAnswers();
if (!newAnswerIds.isEmpty()) {
final List<Comment> newAnswers = commentService.getAllById(newAnswerIds).stream()
.filter(comment -> !oldAnswerIds.contains(comment.getId()))
.collect(Collectors.toList());
changeService.add(
AnswerCommentChange.builder()
.telegramIds(
Collections.singleton(newComment.getAuthor().getTelegramId())
)
.url(newComment.getPullRequest().getUrl())
.youMessage(newComment.getMessage())
.answers(
newAnswers.stream()
.map(comment -> Answer.of(comment.getAuthor().getFullName(), comment.getMessage()))
.collect(Collectors.toList())
)
.build()
);
}
}
}

View File

@ -0,0 +1,49 @@
package org.sadtech.bot.bitbucketbot.service.parser;
import lombok.RequiredArgsConstructor;
import org.sadtech.bot.bitbucketbot.config.properties.BitbucketProperty;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.UserJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.sheet.UserSheetJson;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PersonParser {
private final PersonService personService;
private final ConversionService conversionService;
private final BitbucketProperty bitbucketProperty;
public void scanNewPerson() {
Optional<UserSheetJson> sheetJson = Utils.urlToJson(bitbucketProperty.getUrlUsers(), bitbucketProperty.getToken(), UserSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().hasContent()) {
final UserSheetJson sheetUsers = sheetJson.get();
final List<UserJson> users = sheetUsers.getValues();
final Set<String> logins = users.stream().map(UserJson::getName).collect(Collectors.toSet());
final Set<String> existsLogins = personService.existsByLogin(logins);
final Set<Person> newUsers = users.stream()
.filter(userJson -> !existsLogins.contains(userJson.getName()))
.map(userJson -> conversionService.convert(userJson, Person.class))
.collect(Collectors.toSet());
if (!newUsers.isEmpty()) {
personService.createAll(newUsers);
}
if (sheetUsers.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(bitbucketProperty.getUrlUsers() + sheetUsers.getNextPageStart(), bitbucketProperty.getToken(), UserSheetJson.class);
} else {
break;
}
}
}
}

View File

@ -0,0 +1,128 @@
package org.sadtech.bot.bitbucketbot.service.parser;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sadtech.bot.bitbucketbot.config.properties.BitbucketProperty;
import org.sadtech.bot.bitbucketbot.domain.IdAndStatusPr;
import org.sadtech.bot.bitbucketbot.domain.PullRequestStatus;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.filter.PullRequestFilter;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.PullRequestJson;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.sheet.PullRequestSheetJson;
import org.sadtech.bot.bitbucketbot.service.PersonService;
import org.sadtech.bot.bitbucketbot.service.PullRequestsService;
import org.sadtech.bot.bitbucketbot.service.Utils;
import org.sadtech.bot.bitbucketbot.utils.Pair;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.sadtech.bot.bitbucketbot.domain.PullRequestStatus.DECLINED;
import static org.sadtech.bot.bitbucketbot.domain.PullRequestStatus.MERGED;
import static org.sadtech.bot.bitbucketbot.domain.PullRequestStatus.OPEN;
@Slf4j
@Service
@RequiredArgsConstructor
public class PullRequestParser {
private static final Set<PullRequestStatus> STATUSES = Stream.of(MERGED, OPEN, DECLINED).collect(Collectors.toSet());
private final PullRequestsService pullRequestsService;
private final PersonService personService;
private final ConversionService conversionService;
private final BitbucketProperty bitbucketProperty;
public void parsingOldPullRequest() {
final Set<Long> existsId = pullRequestsService.getAllId(STATUSES).stream()
.map(IdAndStatusPr::getId)
.collect(Collectors.toSet());
final Set<Long> openId = parsingPullRequest(bitbucketProperty.getUrlPullRequestOpen());
final Set<Long> closeId = parsingPullRequest(bitbucketProperty.getUrlPullRequestClose());
final Set<Long> newNotExistsId = existsId.stream()
.filter(id -> !openId.contains(id) && !closeId.contains(id))
.collect(Collectors.toSet());
log.info("Открыты: " + Arrays.toString(openId.toArray()));
log.info("Закрыты: " + Arrays.toString(closeId.toArray()));
log.info("Не найдены: " + Arrays.toString(newNotExistsId.toArray()));
if (!newNotExistsId.isEmpty()) {
pullRequestsService.deleteAll(newNotExistsId);
}
}
private Set<Long> parsingPullRequest(@NonNull String url) {
final List<Person> users = personService.getAllRegister();
final Set<Long> ids = new HashSet<>();
for (Person user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(url, user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().hasContent()) {
final PullRequestSheetJson jsonSheet = sheetJson.get();
final List<PullRequest> existsPr = getExistsPr(jsonSheet.getValues());
ids.addAll(
pullRequestsService.updateAll(existsPr).stream()
.map(PullRequest::getId)
.collect(Collectors.toSet())
);
if (jsonSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(url + jsonSheet.getNextPageStart(), bitbucketProperty.getToken(), PullRequestSheetJson.class);
} else {
break;
}
}
}
return ids;
}
private List<PullRequest> getExistsPr(@NonNull List<PullRequestJson> pullRequestJsons) {
return pullRequestJsons.stream()
.filter(json -> pullRequestsService.existsByFilterQuery(bitbucketIdAndPullRequestId(json)))
.map(pullRequestJson -> conversionService.convert(pullRequestJson, PullRequest.class))
.peek(pullRequest -> pullRequestsService.getIdByBitbucketIdAndReposId(pullRequest.getBitbucketId(), pullRequest.getRepositoryId()).ifPresent(pullRequest::setId))
.collect(Collectors.toList());
}
private PullRequestFilter bitbucketIdAndPullRequestId(PullRequestJson json) {
return PullRequestFilter.builder()
.bitbucketId(json.getId())
.bitbucketRepositoryId(json.getFromRef().getRepository().getId())
.build();
}
public void parsingNewPullRequest() {
final List<Person> users = personService.getAllRegister();
for (Person user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(bitbucketProperty.getUrlPullRequestOpen(), user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().hasContent()) {
final PullRequestSheetJson pullRequestBitbucketSheet = sheetJson.get();
final List<PullRequest> newPullRequest = pullRequestBitbucketSheet.getValues().stream()
.collect(Collectors.toMap(pullRequestJson -> new Pair<>(pullRequestJson.getId(), pullRequestJson.getFromRef().getRepository().getId()), pullRequestJson -> pullRequestJson))
.values()
.stream()
.filter(pullRequestJson -> !pullRequestsService.existsByFilterQuery(bitbucketIdAndPullRequestId(pullRequestJson)))
.map(pullRequestJson -> conversionService.convert(pullRequestJson, PullRequest.class))
.collect(Collectors.toList());
pullRequestsService.createAll(newPullRequest);
if (pullRequestBitbucketSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(bitbucketProperty.getUrlPullRequestOpen() + pullRequestBitbucketSheet.getNextPageStart(), bitbucketProperty.getToken(), PullRequestSheetJson.class);
} else {
break;
}
}
}
}
}

View File

@ -0,0 +1,84 @@
package org.sadtech.bot.bitbucketbot.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import org.sadtech.bot.bitbucketbot.domain.ReviewerStatus;
import org.sadtech.bot.bitbucketbot.domain.change.Change;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.NewPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ReviewersPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.UpdatePrChange;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.entity.Reviewer;
import org.sadtech.bot.bitbucketbot.domain.util.ReviewerChange;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ChangeGenerator {
public static NewPrChange create(@NonNull PullRequest newPullRequest) {
return NewPrChange.builder()
.author(newPullRequest.getAuthor().getFullName())
.description(newPullRequest.getDescription())
.title(newPullRequest.getTitle())
.url(newPullRequest.getUrl())
.telegramIds(
newPullRequest.getReviewers().stream()
.map(reviewer -> reviewer.getUser().getTelegramId())
.collect(Collectors.toSet())
)
.build();
}
public static UpdatePrChange createUpdatePr(@NonNull PullRequest oldPullRequest, @NonNull PullRequest newPullRequest) {
return UpdatePrChange.builder()
.author(oldPullRequest.getAuthor().getFullName())
.name(newPullRequest.getAuthor().getFullName())
.telegramIds(
newPullRequest.getReviewers().stream()
.map(reviewer -> reviewer.getUser().getTelegramId())
.collect(Collectors.toSet())
)
.url(newPullRequest.getUrl())
.build();
}
public static Change createReviewersPr(@NonNull PullRequest oldPullRequest, @NonNull PullRequest newPullRequest) {
final Map<Long, Reviewer> oldReviewers = oldPullRequest.getReviewers().stream()
.collect(Collectors.toMap(Reviewer::getId, reviewer -> reviewer));
final Map<Long, Reviewer> newReviewers = newPullRequest.getReviewers().stream()
.collect(Collectors.toMap(Reviewer::getId, reviewer -> reviewer));
final List<ReviewerChange> reviewerChanges = new ArrayList<>();
for (Reviewer newReviewer : newReviewers.values()) {
if (oldReviewers.containsKey(newReviewer.getId())) {
final Reviewer oldReviewer = oldReviewers.get(newReviewer.getId());
final ReviewerStatus oldStatus = oldReviewer.getStatus();
final ReviewerStatus newStatus = newReviewer.getStatus();
if (!oldStatus.equals(newStatus)) {
reviewerChanges.add(ReviewerChange.ofOld(oldReviewer.getUser().getFullName(), oldStatus, newStatus));
}
} else {
reviewerChanges.add(ReviewerChange.ofNew(newReviewer.getUser().getFullName(), newReviewer.getStatus()));
}
}
final Set<Long> oldIds = oldReviewers.keySet();
oldIds.removeAll(newReviewers.keySet());
reviewerChanges.addAll(
oldReviewers.entrySet().stream()
.filter(e -> oldIds.contains(e.getKey()))
.map(e -> ReviewerChange.ofDeleted(e.getValue().getUser().getFullName()))
.collect(Collectors.toList())
);
return ReviewersPrChange.builder()
.title(newPullRequest.getTitle())
.url(newPullRequest.getUrl())
.telegramId(newPullRequest.getAuthor().getTelegramId())
.reviewerChanges(reviewerChanges)
.build();
}
}

View File

@ -0,0 +1,23 @@
package org.sadtech.bot.bitbucketbot.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.sadtech.bot.bitbucketbot.domain.TaskStatus;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentState;
import org.sadtech.bot.bitbucketbot.exception.NotFoundException;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Converter {
public static TaskStatus taskStatus(CommentState commentState) {
switch (commentState) {
case OPEN:
return TaskStatus.OPEN;
case RESOLVED:
return TaskStatus.RESOLVED;
default:
throw new NotFoundException("Неизвестный статус задачи");
}
}
}

View File

@ -1,13 +1,14 @@
package org.sadtech.bot.bitbucketbot.utils;
import lombok.NonNull;
import org.sadtech.bot.bitbucketbot.domain.change.AnswerCommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.CommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.ConflictPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.NewPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.ReviewersPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.StatusPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.UpdatePrChange;
import org.sadtech.bot.bitbucketbot.domain.change.comment.AnswerCommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.comment.CommentChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ConflictPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.NewPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.ReviewersPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.StatusPrChange;
import org.sadtech.bot.bitbucketbot.domain.change.pullrequest.UpdatePrChange;
import org.sadtech.bot.bitbucketbot.domain.change.task.TaskChange;
import org.sadtech.bot.bitbucketbot.domain.entity.PullRequest;
import org.sadtech.bot.bitbucketbot.domain.util.ReviewerChange;
import org.sadtech.bot.bitbucketbot.dto.bitbucket.CommentJson;
@ -40,7 +41,7 @@ public class Message {
@NonNull
public static String generate(NewPrChange newPrChange) {
String message = Smile.FUN + " *Новый Pull Request*" + Smile.BR +
link(newPrChange.getName(), newPrChange.getUrl()) +
link(newPrChange.getTitle(), newPrChange.getUrl()) +
Smile.HR;
if (newPrChange.getDescription() != null && !"".equals(newPrChange.getDescription())) {
message += newPrChange.getDescription() + Smile.HR;
@ -51,7 +52,7 @@ public class Message {
public static String generate(@NonNull StatusPrChange change) {
return Smile.PEN + " *Изменился статус вашего ПР*" + Smile.HR +
link(change.getName(), change.getUrl()) + Smile.BR +
link(change.getTitle(), change.getUrl()) + Smile.BR +
change.getOldStatus().name() + " -> " + change.getNewStatus().name() +
Smile.TWO_BR;
}
@ -89,13 +90,13 @@ public class Message {
final String createMessage = stringBuilder.toString();
return Smile.PEN + " *Изменения ревьюверов вашего ПР*" +
Smile.HR +
link(reviewersChange.getName(), reviewersChange.getUrl()) + Smile.BR +
link(reviewersChange.getTitle(), reviewersChange.getUrl()) + Smile.BR +
createMessage;
}
public static String generate(@NonNull UpdatePrChange change) {
return Smile.UPDATE + " *Обновление Pull Request*" + Smile.BR +
link(change.getName(), change.getUrl()) +
link(change.getTitle(), change.getUrl()) +
Smile.HR +
Smile.AUTHOR + ": " + change.getAuthor() +
Smile.TWO_BR;
@ -103,7 +104,7 @@ public class Message {
public static String generate(@NonNull ConflictPrChange change) {
return Smile.DANGEROUS + "*Внимание конфликт в ПР*" + Smile.HR +
link(change.getName(), change.getUrl()) + Smile.TWO_BR;
link(change.getTitle(), change.getUrl()) + Smile.TWO_BR;
}
@NonNull
@ -132,7 +133,6 @@ public class Message {
.append(Smile.BR);
}
message
.append(Smile.BR)
.append("Удачного дня ").append(Smile.FLOWER).append(Smile.TWO_BR);
return message.toString();
}
@ -164,6 +164,21 @@ public class Message {
return message.toString();
}
public static String generateNewTask(@NonNull TaskChange newTaskChange) {
return Smile.TASK + "*Назначена новая задача* | " + link("ПР", newTaskChange.getUrl()) + Smile.HR +
newTaskChange.getAuthorName() + ": " + newTaskChange.getMessageTask();
}
public static String generateDeleteTask(@NonNull TaskChange deletedTaskChange) {
return Smile.TASK + "*Задача была удалена* | " + link("ПР", deletedTaskChange.getUrl()) + Smile.HR +
deletedTaskChange.getAuthorName() + ": " + deletedTaskChange.getMessageTask();
}
public static String generateResolveTask(@NonNull TaskChange deletedTaskChange) {
return Smile.TASK + "*Задача выполнена* | " + link("ПР", deletedTaskChange.getUrl()) + Smile.HR +
deletedTaskChange.getAuthorName() + ": " + deletedTaskChange.getMessageTask();
}
private static String needWorkPr(@NonNull List<PullRequest> pullRequestsNeedWork) {
final StringBuilder message = new StringBuilder();
pullRequestsNeedWork.stream()

View File

@ -1,7 +1,7 @@
package org.sadtech.bot.bitbucketbot.utils;
import lombok.NonNull;
import org.sadtech.bot.bitbucketbot.domain.entity.User;
import org.sadtech.bot.bitbucketbot.domain.entity.Person;
import java.util.Collections;
import java.util.Set;
@ -12,7 +12,7 @@ public class NonNullUtils {
throw new IllegalStateException("Утилитный класс");
}
public static Set<Long> telegramIdByUser(@NonNull User user) {
public static Set<Long> telegramIdByUser(@NonNull Person user) {
return user.getTelegramId() != null ? Collections.singleton(user.getTelegramId()) : Collections.emptySet();
}

View File

@ -26,6 +26,7 @@ public enum Smile {
DAY_3("\uD83C\uDF18"),
DAY_4("\uD83C\uDF11"),
DAY_5("\uD83C\uDF1A"),
TASK("\uD83E\uDD39\uD83C\uDFFB\u200D♂"),
MEGA_FUN("\uD83D\uDE02"),
DANGEROUS("⚠️"),
BELL("\uD83D\uDECE"),

View File

@ -17,8 +17,13 @@ spring:
lob:
non_contextual_creation: true
bitbucketbot:
scheduler:
comment:
settings:
no-comment-count: 20
comment-count: 100
init:
start-comment-id:
start-comment-id: 5947
server-send:
url: http://188.225.35.149:8080/api/send
bitbucket:
@ -27,3 +32,4 @@ bitbucketbot:
url-pull-request-close: http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=150&closedSince=86400
url-pull-request-comment: http://192.168.236.164:7990/rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}
url-pull-request: http://192.168.236.164:7990/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/overview
url-users: http://192.168.236.164:7990/rest/api/1.0/admin/users

View File

@ -27,3 +27,4 @@ bitbucketbot:
url-pull-request-close: http://localhost:7990/rest/api/1.0/dashboard/pull-requests?limit=150&closedSince=86400
url-pull-request-comment: http://localhost:7990/rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}
url-pull-request: http://localhost:7990/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/overview
url-users: http://localhost:7990/rest/api/1.0/admin/users

View File

@ -3,10 +3,6 @@
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-3.1.xsd">
<include file="liquibase/change-set/create-table.xml"/>
<include file="liquibase/change-set/v1.2.0.xml"/>
<include file="liquibase/change-set/v1.3.0.xml"/>
<include file="liquibase/change-set/v1.4.0.xml"/>
<include file="liquibase/change-set/v2.0.0.xml"/>
<include file="liquibase/v.2.0.0/cumulative.xml"/>
</databaseChangeLog>

View File

@ -1,55 +0,0 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
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-3.1.xsd">
<changeSet id="create-table" author="upagge">
<createTable tableName="user">
<column name="login" type="varchar(50)">
<constraints primaryKey="true"/>
</column>
<column name="token" type="varchar(200)"/>
<column name="telegram_id" type="integer">
<constraints unique="true"/>
</column>
</createTable>
<createTable tableName="pull_request">
<column name="id" type="integer" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="bitbucket_pr_id" type="integer"/>
<column name="repository_id" type="integer"/>
<column name="author_login" type="varchar(100)">
<constraints nullable="false"
foreignKeyName="login"
references="user(login)"/>
</column>
<column name="status" type="varchar(50)">
<constraints nullable="false"/>
</column>
<column name="url" type="varchar(500)">
<constraints nullable="false"/>
</column>
<column name="name" type="varchar(100)"/>
</createTable>
<addUniqueConstraint tableName="pull_request" columnNames="bitbucket_pr_id, repository_id"/>
<createTable tableName="reviewer">
<column name="id" type="integer" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="pull_request_id" type="integer">
<constraints foreignKeyName="fk_pull_request_id" references="pull_request(id)" deleteCascade="true"/>
</column>
<column name="user_login" type="varchar(100)">
<constraints nullable="false"
foreignKeyName="fk_user_login"
references="user(login)"/>
</column>
<column name="status" type="varchar(50)"/>
</createTable>
<addUniqueConstraint tableName="reviewer" columnNames="pull_request_id, user_login"/>
</changeSet>
</databaseChangeLog>

View File

@ -1,34 +0,0 @@
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="v1.2.0-add-atribute-pr" author="upagge">
<addColumn tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="version" type="integer"/>
</addColumn>
</changeSet>
<changeSet id="default-version" author="upagge">
<update tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="version" value="0" type="integer"/>
</update>
</changeSet>
<changeSet id="date-update-pr" author="upagge">
<addColumn tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="create_date" type="date"/>
</addColumn>
<addColumn tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="update_date" type="date"/>
</addColumn>
</changeSet>
<changeSet id="user-fullname" author="upagge">
<addColumn tableName="user" schemaName="public" catalogName="pg_catalog">
<column name="full_name" type="varchar(50)"/>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@ -1,34 +0,0 @@
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="v1.3.0-add-column-pr" author="upagge">
<addColumn tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="project_key" type="varchar(50)"/>
</addColumn>
<addColumn tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="repository_slug" type="varchar(50)"/>
</addColumn>
<createTable tableName="tech_info">
<column name="surogat_id" type="int">
<constraints primaryKey="true"/>
</column>
<column name="last_comment_id" type="int"/>
</createTable>
<insert tableName="tech_info" schemaName="public" catalogName="pg_catalog">
<column name="surogat_id" value="1"/>
<column name="last_comment_id" value="4500"/>
</insert>
</changeSet>
<changeSet id="v1.3.0-add-pr-description" author="upagge">
<addColumn tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="description" type="varchar(200)"/>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@ -1,54 +0,0 @@
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="create-table-comments" author="upagge">
<createTable tableName="comment">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true"/>
</column>
<column name="url" type="varchar(300)"/>
<column name="telegram" type="integer"/>
<column name="date" type="datetime"/>
</createTable>
</changeSet>
<changeSet id="create-table-comment-tree" author="upagge">
<createTable tableName="comment_tree">
<column name="parent_id" type="int">
<constraints foreignKeyName="fk_parent_id_from_comment_id" references="comment(id)" nullable="false"/>
</column>
<column name="child_id" type="int">
<constraints foreignKeyName="fk_child_id_from_comment_id" references="comment(id)" nullable="false"/>
</column>
</createTable>
<addPrimaryKey tableName="comment_tree" columnNames="parent_id, child_id"/>
</changeSet>
<changeSet id="drop-table" author="upagge">
<dropTable tableName="tech_info"/>
</changeSet>
<changeSet id="dropForeignKeyConstraint-comment" author="upagge">
<dropForeignKeyConstraint baseTableName="comment_tree" constraintName="fk_child_id_from_comment_id"/>
</changeSet>
<changeSet id="modifyDataType-date-up-pr" author="upagge">
<modifyDataType catalogName="pg_catalog"
columnName="create_date"
newDataType="datetime"
schemaName="public"
tableName="pull_request"/>
</changeSet>
<changeSet id="modifyDataType-date-create-pr" author="upagge">
<modifyDataType catalogName="pg_catalog"
columnName="update_date"
newDataType="datetime"
schemaName="public"
tableName="pull_request"/>
</changeSet>
</databaseChangeLog>

View File

@ -1,24 +0,0 @@
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="add-column-pr-conflict" author="upagge">
<addColumn tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="conflict" type="boolean"/>
</addColumn>
</changeSet>
<changeSet id="default-pr-conflict" author="upagge">
<update tableName="pull_request" schemaName="public" catalogName="pg_catalog">
<column name="conflict" value="false" type="boolean"/>
</update>
</changeSet>
<changeSet id="add-column-comment-pr_url" author="upagge">
<addColumn tableName="comment" schemaName="public" catalogName="pg_catalog">
<column name="pr_url" type="varchar(300)"/>
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@ -0,0 +1,162 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
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-3.1.xsd">
<changeSet id="create-table-person" author="upagge">
<createTable tableName="person">
<column name="login" type="varchar(64)">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="full_name" type="varchar(100)">
<constraints nullable="false"/>
</column>
<column name="bitbucket_token" type="varchar(200)"/>
<column name="telegram_id" type="integer">
<constraints unique="true"/>
</column>
</createTable>
</changeSet>
<changeSet id="create-table-pull-request" author="upagge">
<createTable tableName="pull_request">
<column name="id" type="integer" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="bitbucket_id" type="integer">
<constraints nullable="false"/>
</column>
<column name="repository_id" type="integer"/>
<column name="project_key" type="varchar(100)">
<constraints nullable="false"/>
</column>
<column name="repository_slug" type="varchar(100)">
<constraints nullable="false"/>
</column>
<column name="title" type="varchar(100)"/>
<column name="description" type="varchar(300)"/>
<column name="author_login" type="varchar(64)">
<constraints nullable="false"
foreignKeyName="pull_request_author_login_person_login"
references="person(login)" deleteCascade="true"/>
</column>
<column name="status" type="varchar(50)">
<constraints nullable="false"/>
</column>
<column name="url" type="varchar(500)">
<constraints nullable="false"/>
</column>
<column name="conflict" type="boolean">
<constraints nullable="false"/>
</column>
<column name="bitbucket_version" type="int">
<constraints nullable="false"/>
</column>
<column name="create_date" type="datetime">
<constraints nullable="false"/>
</column>
<column name="update_date" type="datetime">
<constraints nullable="false"/>
</column>
</createTable>
<addUniqueConstraint tableName="pull_request" columnNames="bitbucket_pr_id, repository_id"/>
</changeSet>
<changeSet id="create-table-pull-request-reviewer" author="upagge">
<createTable tableName="pull_request_reviewer">
<column name="id" type="integer" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="pull_request_id" type="integer">
<constraints nullable="false" foreignKeyName="pull_request_reviewer_pull_request_id_pull_request_id"
references="pull_request(id)" deleteCascade="true"/>
</column>
<column name="user_login" type="varchar(64)">
<constraints nullable="false"
foreignKeyName="pull_request_reviewer_user_id_user_login"
references="person(login)" deleteCascade="true"/>
</column>
<column name="status" type="varchar(50)">
<constraints nullable="false"/>
</column>
</createTable>
<addUniqueConstraint tableName="reviewer" columnNames="pull_request_id, user_login"/>
</changeSet>
<changeSet id="create-table-comments" author="upagge">
<createTable tableName="pull_request_comment">
<column name="id" type="int">
<constraints primaryKey="true"/>
</column>
<column name="url" type="varchar(300)"/>
<column name="author_login" type="integer">
<constraints nullable="false" foreignKeyName="pull_request_task_author_login_person_login"
references="person(login)" deleteCascade="true"/>
</column>
<column name="pull_request_id" type="integer">
<constraints nullable="false" foreignKeyName="pull_request_comment_pull_request_id_pull_request_id"
references="pull_request(id)" deleteCascade="true"/>
</column>
<column name="message" type="varchar(500)"/>
<column name="create_date" type="datetime">
<constraints nullable="false"/>
</column>
<column name="bitbucket_version" type="int">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
<changeSet id="create-table-comment-tree" author="upagge">
<createTable tableName="comment_tree">
<column name="parent_id" type="int">
<constraints foreignKeyName="fk_parent_id_from_comment_id" references="comment(id)" nullable="false"
deleteCascade="true"/>
</column>
<column name="child_id" type="int">
<constraints foreignKeyName="fk_child_id_from_comment_id" references="comment(id)" nullable="false"
deleteCascade="true"/>
</column>
</createTable>
<addPrimaryKey tableName="comment_tree" columnNames="parent_id, child_id"/>
</changeSet>
<changeSet id="create-table-task" author="upagge">
<createTable tableName="pull_request_task">
<column name="id" type="integer" autoIncrement="true">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="description" type="varchar(500)">
<constraints nullable="false"/>
</column>
<column name="status" type="varchar(64)">
<constraints nullable="false"/>
</column>
<column name="url" type="varchar(300)">
<constraints nullable="false"/>
</column>
<column name="url_api" type="varchar(300)">
<constraints nullable="false"/>
</column>
<column name="author_login" type="varchar(64)">
<constraints nullable="false" foreignKeyName="pull_request_task_author_login_person_login"
references="person(login)" deleteCascade="true"/>
</column>
<column name="pull_request_id" type="int">
<constraints nullable="true" foreignKeyName="pull_request_task_pull_request_id_pull_request_id"
references="pull_request(id)" deleteCascade="true"/>
</column>
<column name="create_date" type="datetime">
<constraints nullable="false"/>
</column>
<column name="bitbucket_version" type="int">
<constraints nullable="false"/>
</column>
</createTable>
</changeSet>
</databaseChangeLog>

View File

@ -0,0 +1,8 @@
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<include file="liquibase/v.2.0.0/create-table.xml"/>
</databaseChangeLog>

View File

@ -18,7 +18,7 @@
</appender>
<root level="info">
<appender-ref ref="FILE"/>
<appender-ref ref="STDOUT"/>
</root>
</configuration>