Первая рабочая версия

This commit is contained in:
Mark Struchkov 2020-02-10 11:50:18 +03:00
parent 71de4746fd
commit 2739efff32
15 changed files with 402 additions and 108 deletions

View File

@ -0,0 +1,15 @@
package com.tsc.bitbucketbot.bitbucket;
import lombok.Data;
/**
* TODO: Добавить описание класса.
*
* @author upagge [04.02.2020]
*/
@Data
public class FromRefJson {
private RepositoryJson repository;
}

View File

@ -20,5 +20,6 @@ public class PullRequestJson {
private LinkJson links; private LinkJson links;
private UserDecisionJson author; private UserDecisionJson author;
private List<UserDecisionJson> reviewers; private List<UserDecisionJson> reviewers;
private FromRefJson fromRef;
} }

View File

@ -0,0 +1,15 @@
package com.tsc.bitbucketbot.bitbucket;
import lombok.Data;
/**
* TODO: Добавить описание класса.
*
* @author upagge [04.02.2020]
*/
@Data
public class RepositoryJson {
private Long id;
}

View File

@ -1,12 +1,23 @@
package com.tsc.bitbucketbot.domain; package com.tsc.bitbucketbot.domain;
import lombok.Getter;
/** /**
* TODO: Добавить комментарий енума. * TODO: Добавить комментарий енума.
* *
* @author upagge [01.02.2020] * @author upagge [01.02.2020]
*/ */
@Getter
public enum ReviewerStatus { public enum ReviewerStatus {
NEEDS_WORK, APPROVED, UNAPPROVED NEEDS_WORK("'NEEDS WORK'"),
APPROVED("'APPROVED'"),
UNAPPROVED("'UNAPPROVED'");
private String value;
ReviewerStatus(String value) {
this.value = value;
}
} }

View File

@ -32,21 +32,29 @@ import java.util.List;
@Setter @Setter
@Entity @Entity
@Builder @Builder
@NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(of = "id") @EqualsAndHashCode(of = "id")
@ToString @ToString
public class PullRequest { public class PullRequest {
@Id @Id
@Column(name = "id") @Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@Column(name = "bitbucket_pr_id")
private Long bitbucketId;
@Column(name = "repository_id")
private Long repositoryId;
@ManyToOne @ManyToOne
@JoinColumn(name = "autor_login") @JoinColumn(name = "author_login")
private User author; private User author;
@OneToMany(mappedBy = "pullRequestId", cascade = CascadeType.ALL, fetch = FetchType.EAGER) @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@JoinColumn(name = "pull_request_id")
private List<Reviewer> reviewers; private List<Reviewer> reviewers;
@Column(name = "url") @Column(name = "url")
@ -59,4 +67,20 @@ public class PullRequest {
@Column(name = "status") @Column(name = "status")
private PullRequestStatus status; private PullRequestStatus status;
// public void setReviewers(List<Reviewer> reviewers) {
// this.reviewers = reviewers;
// this.reviewers.forEach(reviewer -> reviewer.setPullRequest(this));
// }
//
// public PullRequest(Long bitbucketId, Long repositoryId, User author, List<Reviewer> reviewers, String url, String name, PullRequestStatus status) {
// this.bitbucketId = bitbucketId;
// this.repositoryId = repositoryId;
// this.author = author;
// this.reviewers = reviewers;
// this.url = url;
// this.name = name;
// this.status = status;
// this.reviewers.forEach(reviewer -> reviewer.setPullRequest(this));
// }
} }

View File

@ -35,13 +35,6 @@ public class Reviewer {
@Column(name = "id") @Column(name = "id")
private Long id; private Long id;
// @OneToMany(fetch = FetchType.LAZY)
// @JoinColumn(name = "pull_request_id")
@Column(name = "pull_request_id")
private Long pullRequestId;
// @OneToMany(fetch = FetchType.LAZY)
// @JoinColumn(name = "user_login")
@Column(name = "user_login") @Column(name = "user_login")
private String user; private String user;
@ -49,5 +42,4 @@ public class Reviewer {
@Column(name = "status") @Column(name = "status")
private ReviewerStatus status; private ReviewerStatus status;
} }

View File

@ -0,0 +1,57 @@
package com.tsc.bitbucketbot.domain.util;
import com.tsc.bitbucketbot.domain.ReviewerStatus;
import lombok.Getter;
import lombok.NonNull;
/**
* TODO: Добавить описание класса.
*
* @author upagge [07.02.2020]
*/
@Getter
public class ReviewerChange {
private String name;
private Type type;
private ReviewerStatus status;
private ReviewerStatus oldStatus;
private ReviewerChange(String name, Type type, ReviewerStatus status) {
this.name = name;
this.type = type;
this.status = status;
}
private ReviewerChange(String name, Type type, ReviewerStatus status, ReviewerStatus oldStatus) {
this.name = name;
this.type = type;
this.status = status;
this.oldStatus = oldStatus;
}
private ReviewerChange(String name, Type type) {
this.name = name;
this.type = type;
}
@NonNull
public static ReviewerChange ofNew(String name, ReviewerStatus reviewerStatus) {
return new ReviewerChange(name, Type.NEW, reviewerStatus);
}
@NonNull
public static ReviewerChange ofOld(String name, ReviewerStatus oldStatus, ReviewerStatus newStatus) {
return new ReviewerChange(name, Type.OLD, newStatus, oldStatus);
}
@NonNull
public static ReviewerChange ofDeleted(String name) {
return new ReviewerChange(name, Type.DELETED);
}
public enum Type {
NEW, DELETED, OLD
}
}

View File

@ -2,7 +2,11 @@ package com.tsc.bitbucketbot.repository;
import com.tsc.bitbucketbot.domain.entity.PullRequest; import com.tsc.bitbucketbot.domain.entity.PullRequest;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.Collection;
import java.util.Optional;
import java.util.Set; import java.util.Set;
/** /**
@ -14,4 +18,11 @@ public interface PullRequestsRepository extends JpaRepository<PullRequest, Long>
Set<PullRequest> findAllByIdIn(Set<Long> ids); Set<PullRequest> findAllByIdIn(Set<Long> ids);
Boolean existsByBitbucketIdAndRepositoryId(Long bitbucketId, Long repositoryId);
@Query("SELECT p.id FROM PullRequest p WHERE p.bitbucketId=:bitbucketId AND p.repositoryId=:repositoryId")
Optional<Long> findIdByBitbucketIdAndRepositoryId(@Param("bitbucketId") Long bitbucketId, @Param("repositoryId") Long repositoryId);
void deleteAllByIdIn(Collection<Long> id);
} }

View File

@ -1,7 +1,6 @@
package com.tsc.bitbucketbot.scheduler; package com.tsc.bitbucketbot.scheduler;
import com.tsc.bitbucketbot.bitbucket.PullRequestJson; import com.tsc.bitbucketbot.bitbucket.PullRequestJson;
import com.tsc.bitbucketbot.bitbucket.UserDecisionJson;
import com.tsc.bitbucketbot.bitbucket.sheet.PullRequestSheetJson; import com.tsc.bitbucketbot.bitbucket.sheet.PullRequestSheetJson;
import com.tsc.bitbucketbot.config.BitbucketConfig; import com.tsc.bitbucketbot.config.BitbucketConfig;
import com.tsc.bitbucketbot.domain.PullRequestStatus; import com.tsc.bitbucketbot.domain.PullRequestStatus;
@ -9,10 +8,13 @@ import com.tsc.bitbucketbot.domain.ReviewerStatus;
import com.tsc.bitbucketbot.domain.entity.PullRequest; import com.tsc.bitbucketbot.domain.entity.PullRequest;
import com.tsc.bitbucketbot.domain.entity.Reviewer; import com.tsc.bitbucketbot.domain.entity.Reviewer;
import com.tsc.bitbucketbot.domain.entity.User; import com.tsc.bitbucketbot.domain.entity.User;
import com.tsc.bitbucketbot.domain.util.ReviewerChange;
import com.tsc.bitbucketbot.service.PullRequestsService; import com.tsc.bitbucketbot.service.PullRequestsService;
import com.tsc.bitbucketbot.service.UserService; import com.tsc.bitbucketbot.service.UserService;
import com.tsc.bitbucketbot.service.Utils; import com.tsc.bitbucketbot.service.Utils;
import com.tsc.bitbucketbot.service.converter.PullRequestJsonConverter; import com.tsc.bitbucketbot.service.converter.PullRequestJsonConverter;
import com.tsc.bitbucketbot.utils.Message;
import javafx.util.Pair;
import lombok.NonNull; import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.sadtech.social.core.domain.BoxAnswer; import org.sadtech.social.core.domain.BoxAnswer;
@ -21,9 +23,11 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -37,8 +41,8 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor @RequiredArgsConstructor
public class SchedulerPullRequest { public class SchedulerPullRequest {
private static final String URL_NEW_PR = "http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=150&state=OPEN"; private static final String URL_OPEN_PR = "http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=150&state=OPEN";
private static final String URL_OLD_PR = "http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=150&closedSince=86400"; private static final String URL_CLOSE_PR = "http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=150&closedSince=86400";
private final BitbucketConfig bitbucketConfig; private final BitbucketConfig bitbucketConfig;
private final PullRequestsService pullRequestsService; private final PullRequestsService pullRequestsService;
private final UserService userService; private final UserService userService;
@ -46,34 +50,48 @@ public class SchedulerPullRequest {
private final Sending sending; private final Sending sending;
@Scheduled(fixedRate = 15000) @Scheduled(fixedRate = 15000)
public void checkOldPullRequest() { public void checkClosePullRequest() {
final List<User> users = userService.getAllRegistered(); final List<User> users = userService.getAllRegistered();
for (User user : users) { for (User user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(URL_OLD_PR, user.getToken(), PullRequestSheetJson.class); Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(URL_CLOSE_PR, user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) { while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) {
final PullRequestSheetJson pullRequestBitbucketSheet = sheetJson.get(); final PullRequestSheetJson pullRequestBitbucketSheet = sheetJson.get();
Set<Long> existsId = pullRequestsService.existsById( final List<PullRequestJson> bitbucketPullRequests = pullRequestBitbucketSheet.getValues().stream()
pullRequestBitbucketSheet.getValues().stream() .filter(
.map(PullRequestJson::getId) pullRequestJson -> pullRequestsService.existsByBitbucketIdAndReposId(
.collect(Collectors.toSet()) pullRequestJson.getId(),
); pullRequestJson.getFromRef().getRepository().getId()
final Map<Long, PullRequestJson> existsPullRequestBitbucket = pullRequestBitbucketSheet.getValues().stream() )
.filter(pullRequestJson -> existsId.contains(pullRequestJson.getId())) )
.collect(Collectors.toMap(PullRequestJson::getId, pullRequestJson -> pullRequestJson)); .collect(Collectors.toList());
final Set<PullRequest> pullRequests = pullRequestsService.getAllById(existsId); final Set<Long> pullRequestId = bitbucketPullRequests.stream()
if (!existsPullRequestBitbucket.isEmpty() && !pullRequests.isEmpty()) { .map(
processingUpdate(existsPullRequestBitbucket, pullRequests); pullRequestJson -> pullRequestsService.getIdByBitbucketIdAndReposId(
pullRequestJson.getId(),
pullRequestJson.getFromRef().getRepository().getId()
)
)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
for (PullRequestJson bitbucketPullRequest : bitbucketPullRequests) {
final Optional<User> optUser = userService.getByLogin(bitbucketPullRequest.getAuthor().getUser().getName());
if (optUser.isPresent()) {
final User author = optUser.get();
final Long telegramId = author.getTelegramId();
if (telegramId != null) {
final PullRequestStatus pullRequestStatus = PullRequestJsonConverter.convertPullRequestStatus(bitbucketPullRequest.getState());
@NonNull final String message = Message.statusPullRequest(bitbucketPullRequest.getTitle(), bitbucketPullRequest.getLinks().getSelf().get(0).getHref(), PullRequestStatus.OPEN, pullRequestStatus);
sending.send(telegramId, BoxAnswer.of(message));
}
}
} }
if (!existsPullRequestBitbucket.isEmpty()) { pullRequestsService.deleteAll(pullRequestId);
pullRequestsService.updateAll(
existsPullRequestBitbucket.values().stream()
.map(pullRequestBitbucket -> conversionService.convert(pullRequestBitbucket, PullRequest.class))
.collect(Collectors.toList())
);
}
if (pullRequestBitbucketSheet.getNextPageStart() != null) { if (pullRequestBitbucketSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(URL_OLD_PR + pullRequestBitbucketSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class); sheetJson = Utils.urlToJson(URL_CLOSE_PR + pullRequestBitbucketSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class);
} else { } else {
break; break;
} }
@ -81,76 +99,113 @@ public class SchedulerPullRequest {
} }
} }
private void processingUpdate(Map<Long, PullRequestJson> existsPullRequestBitbucket, Set<PullRequest> pullRequests) { @Scheduled(fixedRate = 15000)
public void checkOldPullRequest() {
final List<User> users = userService.getAllRegistered();
for (User user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(URL_OPEN_PR, user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) {
final PullRequestSheetJson pullRequestBitbucketSheet = sheetJson.get();
final Map<Long, PullRequest> existsPullRequestBitbucket = pullRequestBitbucketSheet.getValues().stream()
.filter(
pullRequestJson -> pullRequestsService.existsByBitbucketIdAndReposId(
pullRequestJson.getId(),
pullRequestJson.getFromRef().getRepository().getId()
)
)
.map(pullRequestJson -> conversionService.convert(pullRequestJson, PullRequest.class))
.filter(Objects::nonNull)
.peek(pullRequest -> pullRequestsService.getIdByBitbucketIdAndReposId(pullRequest.getBitbucketId(), pullRequest.getRepositoryId()).ifPresent(pullRequest::setId))
.collect(Collectors.toMap(PullRequest::getId, pullRequest -> pullRequest));
final Set<PullRequest> pullRequests = pullRequestsService.getAllById(existsPullRequestBitbucket.keySet());
if (!existsPullRequestBitbucket.isEmpty() && !pullRequests.isEmpty()) {
pullRequestsService.updateAll(processingUpdate(existsPullRequestBitbucket, pullRequests));
}
if (pullRequestBitbucketSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(URL_CLOSE_PR + pullRequestBitbucketSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class);
} else {
break;
}
}
}
}
@NonNull
private List<PullRequest> processingUpdate(Map<Long, PullRequest> newPullRequests, Set<PullRequest> pullRequests) {
List<PullRequest> updatePullRequest = new ArrayList<>();
for (PullRequest pullRequest : pullRequests) { for (PullRequest pullRequest : pullRequests) {
final PullRequestJson pullRequestBitbucket = existsPullRequestBitbucket.get(pullRequest.getId()); final PullRequest newPullRequest = newPullRequests.get(pullRequest.getId());
final User author = pullRequest.getAuthor(); final User author = pullRequest.getAuthor();
StringBuilder stringBuilder = new StringBuilder();
if (author.getTelegramId() != null) { if (author.getTelegramId() != null) {
sendStatusPR(pullRequest, pullRequestBitbucket); changeStatusPR(pullRequest, newPullRequest).ifPresent(stringBuilder::append);
sendReviewersPR(pullRequest, pullRequestBitbucket); changeReviewersPR(pullRequest, newPullRequest).ifPresent(stringBuilder::append);
final String message = stringBuilder.toString();
if (!Message.EMPTY.equalsIgnoreCase(message)) {
updatePullRequest.add(newPullRequest);
sending.send(author.getTelegramId(), BoxAnswer.of(message));
} }
} }
} }
return updatePullRequest;
}
private void sendReviewersPR(PullRequest pullRequest, PullRequestJson pullRequestBitbucket) { @NonNull
final Map<String, Reviewer> oldReviewers = pullRequest.getReviewers().stream().collect(Collectors.toMap(Reviewer::getUser, reviewer -> reviewer)); private Optional<String> changeReviewersPR(PullRequest pullRequest, PullRequest newPullRequest) {
final List<UserDecisionJson> newReviewers = pullRequestBitbucket.getReviewers(); final Map<String, Reviewer> oldReviewers = pullRequest.getReviewers().stream()
for (UserDecisionJson newReviewer : newReviewers) { .collect(Collectors.toMap(Reviewer::getUser, reviewer -> reviewer));
if (oldReviewers.containsKey(newReviewer.getUser().getName())) { final Map<String, Reviewer> newReviewers = newPullRequest.getReviewers().stream()
final Reviewer oldReviewer = oldReviewers.get(newReviewer.getUser().getName()); .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 oldStatus = oldReviewer.getStatus();
final ReviewerStatus newStatus = PullRequestJsonConverter.convertStatusReviewer(newReviewer.getStatus()); final ReviewerStatus newStatus = newReviewer.getStatus();
boolean flag = false;
StringBuilder stringBuilder = new StringBuilder("✏️ *Изменилось решение по вашему ПР*\n")
.append("[").append(pullRequest.getName()).append("](").append(pullRequest.getUrl()).append(")\n");
if (!oldStatus.equals(newStatus)) { if (!oldStatus.equals(newStatus)) {
flag = true; reviewerChanges.add(ReviewerChange.ofOld(oldReviewer.getUser(), oldStatus, newStatus));
stringBuilder.append("\uD83D\uDC68\u200D\uD83D\uDCBB ").append(oldReviewer.getUser()).append(" ")
.append(oldStatus).append(" -> ").append(newStatus).append("\n");
}
if (flag) {
sending.send(pullRequest.getAuthor().getTelegramId(), BoxAnswer.of(stringBuilder.toString()));
} }
} 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)));
return Message.statusReviewers(pullRequest, reviewerChanges);
} }
private void sendStatusPR(PullRequest pullRequest, PullRequestJson pullRequestBitbucket) {
@NonNull
private Optional<String> changeStatusPR(PullRequest pullRequest, PullRequest newPullRequest) {
final PullRequestStatus oldStatus = pullRequest.getStatus(); final PullRequestStatus oldStatus = pullRequest.getStatus();
final PullRequestStatus newStatus = PullRequestJsonConverter.convertPullRequestStatus(pullRequestBitbucket.getState()); final PullRequestStatus newStatus = newPullRequest.getStatus();
if (!oldStatus.equals(newStatus)) { if (!oldStatus.equals(newStatus)) {
StringBuilder stringBuilder = new StringBuilder("✏️ *Изменился статус вашего ПР*\n") return Optional.of(Message.statusPullRequest(pullRequest.getName(), pullRequest.getUrl(), oldStatus, newStatus));
.append("[").append(pullRequest.getName()).append("](").append(pullRequest.getUrl()).append(")\n")
.append(oldStatus.name()).append(" -> ").append(newStatus.name())
.append("\n-- -- -- --\n")
.append("\uD83D\uDCCC: #pullRequest #change")
.append("\n\n");
sending.send(pullRequest.getAuthor().getTelegramId(), BoxAnswer.of(stringBuilder.toString()));
} }
return Optional.empty();
} }
@Scheduled(fixedRate = 15000) @Scheduled(fixedRate = 15000)
public void checkNewPullRequest() { public void checkNewPullRequest() {
final List<User> users = userService.getAllRegistered(); final List<User> users = userService.getAllRegistered();
for (User user : users) { for (User user : users) {
Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(URL_NEW_PR, user.getToken(), PullRequestSheetJson.class); Optional<PullRequestSheetJson> sheetJson = Utils.urlToJson(URL_OPEN_PR, user.getToken(), PullRequestSheetJson.class);
while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) { while (sheetJson.isPresent() && sheetJson.get().getValues() != null && !sheetJson.get().getValues().isEmpty()) {
final PullRequestSheetJson pullRequestBitbucketSheet = sheetJson.get(); final PullRequestSheetJson pullRequestBitbucketSheet = sheetJson.get();
final Set<Long> pullRequestBitbucketId = pullRequestBitbucketSheet.getValues().stream() final List<PullRequest> newPullRequest = pullRequestBitbucketSheet.getValues().stream()
.map(PullRequestJson::getId) .collect(Collectors.toMap(pullRequestJson -> new Pair<>(pullRequestJson.getId(), pullRequestJson.getFromRef().getRepository().getId()), pullRequestJson -> pullRequestJson))
.collect(Collectors.toSet()); .entrySet()
Set<Long> existsId = pullRequestsService.existsAllIdById(pullRequestBitbucketId); .stream()
final Set<PullRequestJson> newPullRequestBitbucket = pullRequestBitbucketSheet.getValues().stream() .filter(test -> !pullRequestsService.existsByBitbucketIdAndReposId(test.getKey().getKey(), test.getKey().getValue()))
.filter(pullRequestJson -> !existsId.contains(pullRequestJson.getId())) .map(test -> conversionService.convert(test.getValue(), PullRequest.class))
.collect(Collectors.toSet()); .collect(Collectors.toList());
final List<PullRequest> newPullRequests = pullRequestsService.addAll( final List<PullRequest> newPullRequests = pullRequestsService.addAll(newPullRequest);
newPullRequestBitbucket.stream()
.map(pullRequestJson -> conversionService.convert(pullRequestJson, PullRequest.class))
.collect(Collectors.toSet())
);
sendNotification(newPullRequests); sendNotification(newPullRequests);
if (pullRequestBitbucketSheet.getNextPageStart() != null) { if (pullRequestBitbucketSheet.getNextPageStart() != null) {
sheetJson = Utils.urlToJson(URL_NEW_PR + pullRequestBitbucketSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class); sheetJson = Utils.urlToJson(URL_OPEN_PR + pullRequestBitbucketSheet.getNextPageStart(), bitbucketConfig.getToken(), PullRequestSheetJson.class);
} else { } else {
break; break;
} }

View File

@ -1,9 +1,9 @@
package com.tsc.bitbucketbot.service; package com.tsc.bitbucketbot.service;
import com.tsc.bitbucketbot.domain.entity.PullRequest; import com.tsc.bitbucketbot.domain.entity.PullRequest;
import com.tsc.bitbucketbot.domain.entity.Reviewer;
import lombok.NonNull; import lombok.NonNull;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -15,14 +15,20 @@ import java.util.Set;
*/ */
public interface PullRequestsService { public interface PullRequestsService {
Set<Long> existsById(@NonNull final Set<Long> pullRequestId); @NonNull
boolean existsByBitbucketIdAndReposId(Long bitbucketId, Long repositoryId);
Set<Long> existsAllIdById(@NonNull Set<Long> pullRequestId);
Set<PullRequest> getAllById(@NonNull Set<Long> pullRequestJsonId); Set<PullRequest> getAllById(@NonNull Set<Long> pullRequestJsonId);
List<PullRequest> addAll(@NonNull Set<PullRequest> pullRequests); List<PullRequest> addAll(@NonNull Collection<PullRequest> pullRequests);
List<PullRequest> updateAll(@NonNull List<PullRequest> pullRequests); List<PullRequest> updateAll(@NonNull Collection<PullRequest> pullRequests);
@NonNull
Optional<Long> getIdByBitbucketIdAndReposId(Long bitbucketId, Long repositoryId);
void deleteAll(@NonNull Set<Long> id);
Optional<PullRequest> update(PullRequest pullRequest);
} }

View File

@ -33,12 +33,13 @@ public class PullRequestJsonConverter implements Converter<PullRequestJson, Pull
@Override @Override
public PullRequest convert(PullRequestJson json) { public PullRequest convert(PullRequestJson json) {
return PullRequest.builder() return PullRequest.builder()
.id(json.getId()) .bitbucketId(json.getId())
.repositoryId(json.getFromRef().getRepository().getId())
.author(this.convertUser(json.getAuthor().getUser())) .author(this.convertUser(json.getAuthor().getUser()))
.name(json.getTitle()) .name(json.getTitle())
.url(json.getLinks().getSelf().get(0).getHref()) .url(json.getLinks().getSelf().get(0).getHref())
.status(convertPullRequestStatus(json.getState())) .status(convertPullRequestStatus(json.getState()))
.reviewers(convertReviewers(json.getId(), json.getReviewers())) .reviewers(convertReviewers(json.getReviewers()))
.build(); .build();
} }
@ -58,12 +59,11 @@ public class PullRequestJsonConverter implements Converter<PullRequestJson, Pull
return null; return null;
} }
private List<Reviewer> convertReviewers(Long id, List<UserDecisionJson> jsonReviewers) { private List<Reviewer> convertReviewers(List<UserDecisionJson> jsonReviewers) {
return jsonReviewers.stream() return jsonReviewers.stream()
.map( .map(
jsonReviewer -> { jsonReviewer -> {
final Reviewer reviewer = new Reviewer(); final Reviewer reviewer = new Reviewer();
reviewer.setPullRequestId(id);
reviewer.setUser(jsonReviewer.getUser().getName()); reviewer.setUser(jsonReviewer.getUser().getName());
reviewer.setStatus(convertStatusReviewer(jsonReviewer.getStatus())); reviewer.setStatus(convertStatusReviewer(jsonReviewer.getStatus()));
return reviewer; return reviewer;
@ -85,5 +85,4 @@ public class PullRequestJsonConverter implements Converter<PullRequestJson, Pull
} }
} }

View File

@ -1,7 +1,6 @@
package com.tsc.bitbucketbot.service.impl; package com.tsc.bitbucketbot.service.impl;
import com.tsc.bitbucketbot.domain.entity.PullRequest; import com.tsc.bitbucketbot.domain.entity.PullRequest;
import com.tsc.bitbucketbot.domain.entity.Reviewer;
import com.tsc.bitbucketbot.repository.PullRequestsRepository; import com.tsc.bitbucketbot.repository.PullRequestsRepository;
import com.tsc.bitbucketbot.service.PullRequestsService; import com.tsc.bitbucketbot.service.PullRequestsService;
import lombok.NonNull; import lombok.NonNull;
@ -9,6 +8,7 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -26,15 +26,8 @@ public class PullRequestsServiceImpl implements PullRequestsService {
private final PullRequestsRepository pullRequestsRepository; private final PullRequestsRepository pullRequestsRepository;
@Override @Override
public Set<Long> existsById(@NonNull Set<Long> idList) { public boolean existsByBitbucketIdAndReposId(@NonNull Long bitbucketId, @NonNull Long repositoryId) {
return idList.stream() return pullRequestsRepository.existsByBitbucketIdAndRepositoryId(bitbucketId, repositoryId);
.filter(pullRequestsRepository::existsById)
.collect(Collectors.toSet());
}
@Override
public Set<Long> existsAllIdById(@NonNull Set<Long> pullRequestJsonId) {
return pullRequestJsonId.stream().filter(pullRequestsRepository::existsById).collect(Collectors.toSet());
} }
@Override @Override
@ -43,13 +36,36 @@ public class PullRequestsServiceImpl implements PullRequestsService {
} }
@Override @Override
public List<PullRequest> addAll(@NonNull Set<PullRequest> pullRequests) { @Transactional
public List<PullRequest> addAll(@NonNull Collection<PullRequest> pullRequests) {
return pullRequestsRepository.saveAll(pullRequests); return pullRequestsRepository.saveAll(pullRequests);
} }
@Override @Override
public List<PullRequest> updateAll(@NonNull List<PullRequest> pullRequests) { public List<PullRequest> updateAll(@NonNull Collection<PullRequest> pullRequests) {
return pullRequestsRepository.saveAll(pullRequests); final List<PullRequest> updatePullRequests = pullRequests.stream()
.filter(pullRequest -> pullRequestsRepository.existsById(pullRequest.getId()))
.collect(Collectors.toList());
return pullRequestsRepository.saveAll(updatePullRequests);
}
@Override
public Optional<PullRequest> update(PullRequest pullRequest) {
if (pullRequestsRepository.existsById(pullRequest.getId())) {
pullRequestsRepository.save(pullRequest);
}
return Optional.empty();
}
@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);
} }
} }

View File

@ -0,0 +1,88 @@
package com.tsc.bitbucketbot.utils;
import com.tsc.bitbucketbot.domain.PullRequestStatus;
import com.tsc.bitbucketbot.domain.entity.PullRequest;
import com.tsc.bitbucketbot.domain.util.ReviewerChange;
import lombok.NonNull;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static com.tsc.bitbucketbot.domain.util.ReviewerChange.Type.DELETED;
import static com.tsc.bitbucketbot.domain.util.ReviewerChange.Type.NEW;
import static com.tsc.bitbucketbot.domain.util.ReviewerChange.Type.OLD;
/**
* TODO: Добавить описание класса.
*
* @author upagge [07.02.2020]
*/
public class Message {
public static final String EMPTY = "";
private static final String BREAK = "\n";
private static final String TWO_BREAK = "\n\n";
private static final String SMILE_AUTHOR = "\uD83D\uDC68\u200D\uD83D\uDCBB";
private static final String SMILE_PEN = "✏️";
private Message() {
throw new IllegalStateException("Утилитарный класс");
}
@NonNull
public static String statusPullRequest(String name, String url, PullRequestStatus oldStatus, PullRequestStatus newStatus) {
return "✏️ *Изменился статус вашего ПР*" + BREAK +
"[" + name + "](" + url + ")" + BREAK +
oldStatus.name() + " -> " + newStatus.name() +
BREAK + "-- -- -- --" + BREAK +
"\uD83D\uDCCC: #pullRequest #change" +
TWO_BREAK;
}
@NonNull
public static Optional<String> statusReviewers(PullRequest pullRequest, List<ReviewerChange> reviewerChanges) {
StringBuilder stringBuilder = new StringBuilder();
final Map<ReviewerChange.Type, List<ReviewerChange>> changes = reviewerChanges.stream()
.collect(Collectors.groupingBy(ReviewerChange::getType));
if (changes.containsKey(OLD)) {
stringBuilder.append(BREAK).append("Изменили свое решение:").append(BREAK);
changes.get(OLD).forEach(
change -> stringBuilder
.append(SMILE_AUTHOR).append(change.getName()).append(": ")
.append(change.getOldStatus().getValue()).append(" -> ")
.append(change.getStatus().getValue())
.append(BREAK)
);
}
if (changes.containsKey(NEW)) {
stringBuilder.append(BREAK).append("Новые ревьюверы:").append(BREAK);
changes.get(NEW).forEach(
change -> stringBuilder
.append(change.getName()).append(" (").append(change.getStatus().getValue()).append(")")
.append(BREAK)
);
}
if (changes.containsKey(DELETED)) {
stringBuilder.append(BREAK).append("Не выдержали ревью:").append(BREAK)
.append(
changes.get(DELETED).stream()
.map(ReviewerChange::getName).collect(Collectors.joining(","))
);
}
final String createMessage = stringBuilder.toString();
if (!EMPTY.equalsIgnoreCase(createMessage)) {
return Optional.of(
SMILE_PEN + " *Изменения ревьюверов вашего ПР*" + BREAK +
"[" + pullRequest.getName() + "](" + pullRequest.getUrl() + ")" + BREAK +
createMessage
+ "\n-- -- -- -- --"
);
}
return Optional.empty();
}
}

View File

@ -22,9 +22,11 @@ bitbucketbot:
bot-token: 1096235968:AAHvIy_mlZJXiNc9aDQWtCuiksz9YGknoXE bot-token: 1096235968:AAHvIy_mlZJXiNc9aDQWtCuiksz9YGknoXE
admin-chatid: 3000811 admin-chatid: 3000811
proxy-config: proxy-config:
host: 167.172.171.134 host: 212.237.23.75
port: 1080 port: 1080
type: SOCKS5 type: SOCKS5
user: upagge
password: seAbotd9Bidu%ZqZB3g4
bitbucket: bitbucket:
token: Nzg5NjUyNDQwMzk2OlA+6naQz02+GxOG0Q9li/jnsn7E token: Nzg5NjUyNDQwMzk2OlA+6naQz02+GxOG0Q9li/jnsn7E
url: http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=50 url: http://192.168.236.164:7990/rest/api/1.0/dashboard/pull-requests?limit=50

View File

@ -18,10 +18,12 @@
</createTable> </createTable>
<createTable tableName="pull_request"> <createTable tableName="pull_request">
<column name="id" type="integer"> <column name="id" type="integer" autoIncrement="true">
<constraints primaryKey="true"/> <constraints primaryKey="true"/>
</column> </column>
<column name="autor_login" type="varchar(100)"> <column name="bitbucket_pr_id" type="integer"/>
<column name="repository_id" type="integer"/>
<column name="author_login" type="varchar(100)">
<constraints nullable="false" <constraints nullable="false"
foreignKeyName="login" foreignKeyName="login"
references="user(login)"/> references="user(login)"/>
@ -34,13 +36,14 @@
</column> </column>
<column name="name" type="varchar(100)"/> <column name="name" type="varchar(100)"/>
</createTable> </createTable>
<addUniqueConstraint tableName="pull_request" columnNames="bitbucket_pr_id, repository_id"/>
<createTable tableName="reviewer"> <createTable tableName="reviewer">
<column name="id" type="integer" autoIncrement="true"> <column name="id" type="integer" autoIncrement="true">
<constraints primaryKey="true"/> <constraints primaryKey="true"/>
</column> </column>
<column name="pull_request_id" type="integer"> <column name="pull_request_id" type="integer">
<constraints nullable="false" foreignKeyName="fk_pull_request_id" references="pull_request(id)"/> <constraints foreignKeyName="fk_pull_request_id" references="pull_request(id)" deleteCascade="true"/>
</column> </column>
<column name="user_login" type="varchar(100)"> <column name="user_login" type="varchar(100)">
<constraints nullable="false" <constraints nullable="false"
@ -50,7 +53,6 @@
<column name="status" type="varchar(50)"/> <column name="status" type="varchar(50)"/>
</createTable> </createTable>
<addUniqueConstraint tableName="reviewer" columnNames="pull_request_id, user_login"/> <addUniqueConstraint tableName="reviewer" columnNames="pull_request_id, user_login"/>
</changeSet> </changeSet>
</databaseChangeLog> </databaseChangeLog>