Merge branch 'develop' into feature/frontend-form-auth

This commit is contained in:
upagge 2020-03-03 10:19:44 +03:00
commit b45b868877
No known key found for this signature in database
GPG Key ID: 15CD012E46F6BA34
21 changed files with 257 additions and 56 deletions

View File

@ -30,12 +30,6 @@
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
@ -46,7 +40,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.liquibase/liquibase-core -->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>

View File

@ -21,6 +21,7 @@ import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import java.time.LocalDate;
import java.util.List;
/**
@ -70,4 +71,10 @@ public class PullRequest {
@Column(name = "status")
private PullRequestStatus status;
@Column(name = "create_date")
private LocalDate createDate;
@Column(name = "update_date")
private LocalDate updateDate;
}

View File

@ -39,4 +39,7 @@ public class User {
@Column(name = "telegram_id")
private Long telegramId;
@Column(name = "full_name")
private String fullName;
}

View File

@ -1,7 +1,10 @@
package com.tsc.bitbucketbot.dto.bitbucket;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.tsc.bitbucketbot.utils.LocalDateFromEpochDeserializer;
import lombok.Data;
import java.time.LocalDate;
import java.util.List;
/**
@ -15,6 +18,13 @@ public class PullRequestJson {
private Long id;
private Integer version;
private PullRequestState state;
@JsonDeserialize(using = LocalDateFromEpochDeserializer.class)
private LocalDate createdDate;
@JsonDeserialize(using = LocalDateFromEpochDeserializer.class)
private LocalDate updatedDate;
private String title;
private String description;
private LinkJson links;

View File

@ -9,4 +9,5 @@ import com.tsc.bitbucketbot.dto.bitbucket.Sheet;
* @author upagge [02.02.2020]
*/
public class PullRequestSheetJson extends Sheet<PullRequestJson> {
}

View File

@ -1,11 +1,13 @@
package com.tsc.bitbucketbot.repository.jpa;
import com.tsc.bitbucketbot.domain.ReviewerStatus;
import com.tsc.bitbucketbot.domain.entity.PullRequest;
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.List;
import java.util.Optional;
import java.util.Set;
@ -25,4 +27,10 @@ public interface PullRequestsRepository extends JpaRepository<PullRequest, Long>
void deleteAllByIdIn(Collection<Long> id);
@Query("SELECT p FROM PullRequest p LEFT JOIN p.reviewers r WHERE r.user=:reviewer AND r.status =:status")
List<PullRequest> findAllByReviewerAndStatuses(@Param("reviewer") String reviewer, @Param("status") ReviewerStatus status);
@Query("SELECT p.id from PullRequest p")
Set<Long> getAllIds();
}

View File

@ -4,6 +4,8 @@ import com.tsc.bitbucketbot.domain.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* TODO: Добавить описание класса.
*
@ -16,4 +18,6 @@ public interface UserRepository extends JpaRepository<User, String> {
boolean existsByLogin(String login);
List<User> findAllByTelegramIdNotNullAndTokenNotNull();
}

View File

@ -0,0 +1,42 @@
package com.tsc.bitbucketbot.scheduler;
import com.tsc.bitbucketbot.domain.MessageSend;
import com.tsc.bitbucketbot.domain.ReviewerStatus;
import com.tsc.bitbucketbot.domain.entity.PullRequest;
import com.tsc.bitbucketbot.domain.entity.User;
import com.tsc.bitbucketbot.service.MessageSendService;
import com.tsc.bitbucketbot.service.PullRequestsService;
import com.tsc.bitbucketbot.service.UserService;
import com.tsc.bitbucketbot.utils.Message;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class SchedulerNotification {
private final UserService userService;
private final PullRequestsService pullRequestsService;
private final MessageSendService messageSendService;
@Scheduled(cron = "0 30 8 * * MON-FRI")
public void goodMorning() {
List<User> allRegister = userService.getAllRegister();
for (User user : allRegister) {
List<PullRequest> pullRequests = pullRequestsService.getAllByReviewerAndStatuses(
user.getLogin(),
ReviewerStatus.NEEDS_WORK
);
messageSendService.add(
MessageSend.builder()
.telegramId(user.getTelegramId())
.message(Message.goodMorningStatistic(user.getFullName(), pullRequests))
.build()
);
}
}
}

View File

@ -24,6 +24,7 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -47,8 +48,18 @@ public class SchedulerPullRequest {
private final BitbucketConfig bitbucketConfig;
@Scheduled(fixedRate = 30000)
public void checkClosePullRequest() {
final List<User> users = userService.getAll();
public void checkOldPullRequest() {
Set<Long> existsId = pullRequestsService.getAllId();
Set<Long> openId = checkOpenPullRequest();
checkClosePullRequest();
existsId.removeAll(openId);
if (!existsId.isEmpty()) {
pullRequestsService.deleteAll(existsId);
}
}
private void checkClosePullRequest() {
final List<User> users = userService.getAllRegister();
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()) {
@ -96,9 +107,9 @@ public class SchedulerPullRequest {
}
}
@Scheduled(fixedRate = 30000)
public void checkOldPullRequest() {
final List<User> users = userService.getAll();
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()) {
@ -116,7 +127,12 @@ public class SchedulerPullRequest {
.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));
processingUpdate(existsPullRequestBitbucket, pullRequests);
ids.addAll(
pullRequestsService.updateAll(existsPullRequestBitbucket.values()).stream()
.map(PullRequest::getId)
.collect(Collectors.toSet())
);
}
if (pullRequestBitbucketSheet.getNextPageStart() != null) {
@ -126,23 +142,19 @@ public class SchedulerPullRequest {
}
}
}
return ids;
}
@NonNull
private List<PullRequest> processingUpdate(Map<Long, PullRequest> newPullRequests, Set<PullRequest> pullRequests) {
List<PullRequest> updatePullRequest = new ArrayList<>();
private void processingUpdate(Map<Long, PullRequest> newPullRequests, Set<PullRequest> pullRequests) {
for (PullRequest pullRequest : pullRequests) {
PullRequest newPullRequest = newPullRequests.get(pullRequest.getId());
@NonNull boolean author = processingAuthor(pullRequest, newPullRequest);
@NonNull boolean reviewer = processingReviewer(pullRequest, newPullRequest);
if (author || reviewer) {
updatePullRequest.add(newPullRequest);
}
processingAuthor(pullRequest, newPullRequest);
processingReviewer(pullRequest, newPullRequest);
}
return updatePullRequest;
}
private boolean processingReviewer(PullRequest pullRequest, PullRequest newPullRequest) {
private void processingReviewer(PullRequest pullRequest, PullRequest newPullRequest) {
StringBuilder stringBuilder = new StringBuilder();
changeVersionPr(pullRequest, newPullRequest).ifPresent(stringBuilder::append);
String message = stringBuilder.toString();
@ -161,13 +173,11 @@ public class SchedulerPullRequest {
newPullRequest.getAuthor().getLogin()))
.build())
);
return true;
}
return false;
}
@NonNull
private boolean processingAuthor(PullRequest pullRequest, PullRequest newPullRequest) {
private void processingAuthor(PullRequest pullRequest, PullRequest newPullRequest) {
final User author = pullRequest.getAuthor();
StringBuilder stringBuilder = new StringBuilder();
if (author.getTelegramId() != null) {
@ -176,10 +186,8 @@ public class SchedulerPullRequest {
final String message = stringBuilder.toString();
if (!Message.EMPTY.equalsIgnoreCase(message)) {
messageSendService.add(MessageSend.builder().message(message).telegramId(author.getTelegramId()).build());
return true;
}
}
return false;
}
@NonNull
@ -237,7 +245,7 @@ public class SchedulerPullRequest {
@Scheduled(fixedRate = 30000)
public void checkNewPullRequest() {
final List<User> users = userService.getAll();
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()) {

View File

@ -1,6 +1,7 @@
package com.tsc.bitbucketbot.scheduler;
import com.google.gson.Gson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tsc.bitbucketbot.config.PushMessageConfig;
import com.tsc.bitbucketbot.domain.MessageSend;
import com.tsc.bitbucketbot.service.MessageSendService;
@ -30,7 +31,7 @@ public class SchedulerPushMessageSend {
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private final MessageSendService messageSendService;
private final Gson gson = new Gson();
private final ObjectMapper objectMapper = new ObjectMapper();
private final PushMessageConfig pushMessageConfig;
private OkHttpClient client;
@ -61,7 +62,11 @@ public class SchedulerPushMessageSend {
public void sendNewMessage() {
List<MessageSend> pushMessage = messageSendService.getPushMessage();
if (!pushMessage.isEmpty()) {
sendMessage(gson.toJson(pushMessage));
try {
sendMessage(objectMapper.writeValueAsString(pushMessage));
} catch (JsonProcessingException e) {
log.error(e.getMessage());
}
}
}

View File

@ -1,5 +1,6 @@
package com.tsc.bitbucketbot.service;
import com.tsc.bitbucketbot.domain.ReviewerStatus;
import com.tsc.bitbucketbot.domain.entity.PullRequest;
import lombok.NonNull;
@ -29,4 +30,9 @@ public interface PullRequestsService {
void deleteAll(@NonNull Set<Long> id);
@NonNull
List<PullRequest> getAllByReviewerAndStatuses(String login, ReviewerStatus statuses);
Set<Long> getAllId();
}

View File

@ -18,12 +18,11 @@ public interface UserService {
Optional<User> getByLogin(String login);
Optional<User> update(User user);
Set<String> existsByLogin(@NonNull Set<String> logins);
Optional<User> reg(@NonNull User user);
List<User> addAll(Set<User> newUsers);
List<User> getAllRegister();
}

View File

@ -1,8 +1,10 @@
package com.tsc.bitbucketbot.service;
import com.google.gson.Gson;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.NonNull;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.IOException;
@ -17,12 +19,22 @@ import java.util.zip.GZIPInputStream;
*
* @author upagge [30.01.2020]
*/
@Service
@Slf4j
public class Utils {
private static Gson gson = new Gson();
private static ObjectMapper objectMapper;
public static <T> Optional<T> urlToJson(@NonNull String urlValue, String token, Class<T> classOfT) {
static {
objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
private Utils() {
throw new IllegalStateException("Утилитарный класс");
}
@NonNull
public static <T> Optional<T> urlToJson(String urlValue, String token, Class<T> classOfT) {
StringBuilder sb = null;
URL url;
URLConnection urlCon;
@ -51,7 +63,11 @@ public class Utils {
}
if (sb != null) {
return Optional.of(gson.fromJson(sb.toString(), classOfT));
try {
return Optional.of(objectMapper.readValue(sb.toString(), classOfT));
} catch (JsonProcessingException e) {
log.error(e.getMessage());
}
}
return Optional.empty();
}

View File

@ -35,6 +35,8 @@ public class PullRequestJsonConverter implements Converter<PullRequestJson, Pull
return PullRequest.builder()
.bitbucketId(json.getId())
.version(json.getVersion())
.createDate(json.getCreatedDate())
.updateDate(json.getUpdatedDate())
.repositoryId(json.getFromRef().getRepository().getId())
.author(this.convertUser(json.getAuthor().getUser()))
.name(json.getTitle())

View File

@ -16,6 +16,7 @@ public class UserJsonConverter implements Converter<UserJson, User> {
@Override
public User convert(UserJson source) {
return User.builder()
.fullName(source.getDisplayName())
.login(source.getName())
.build();
}

View File

@ -1,5 +1,6 @@
package com.tsc.bitbucketbot.service.impl;
import com.tsc.bitbucketbot.domain.ReviewerStatus;
import com.tsc.bitbucketbot.domain.entity.PullRequest;
import com.tsc.bitbucketbot.repository.jpa.PullRequestsRepository;
import com.tsc.bitbucketbot.service.PullRequestsService;
@ -60,4 +61,15 @@ public class PullRequestsServiceImpl implements PullRequestsService {
pullRequestsRepository.deleteAllByIdIn(id);
}
@NonNull
@Override
public List<PullRequest> getAllByReviewerAndStatuses(String login, ReviewerStatus reviewerStatus) {
return pullRequestsRepository.findAllByReviewerAndStatuses(login, reviewerStatus);
}
@Override
public Set<Long> getAllId() {
return pullRequestsRepository.getAllIds();
}
}

View File

@ -38,14 +38,6 @@ public class UserServiceImpl implements UserService {
return userRepository.findById(login);
}
@Override
public Optional<User> update(User user) {
if (userRepository.existsById(user.getLogin())) {
return Optional.of(userRepository.save(user));
}
return Optional.empty();
}
@Override
public Set<String> existsByLogin(@NonNull Set<String> logins) {
return logins.stream().filter(userRepository::existsById).collect(Collectors.toSet());
@ -70,4 +62,9 @@ public class UserServiceImpl implements UserService {
return userRepository.saveAll(newUsers);
}
@Override
public List<User> getAllRegister() {
return userRepository.findAllByTelegramIdNotNullAndTokenNotNull();
}
}

View File

@ -0,0 +1,29 @@
package com.tsc.bitbucketbot.utils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
@Slf4j
public class LocalDateFromEpochDeserializer extends JsonDeserializer<LocalDate> {
@Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt) {
try {
Long time = jp.readValueAs(Long.class);
Instant instant = Instant.ofEpochMilli(time);
LocalDate localDate = instant.atZone(ZoneId.systemDefault()).toLocalDate();
return localDate;
} catch (IOException e) {
log.error(e.getMessage());
}
return LocalDate.now();
}
}

View File

@ -20,12 +20,15 @@ import static com.tsc.bitbucketbot.domain.util.ReviewerChange.Type.*;
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 static final String SMILE_UPDATE = "\uD83D\uDD04";
private static final String HR = "\n -- -- -- -- --\n";
public static final String BREAK = "\n";
public static final String TWO_BREAK = "\n\n";
public static final String SMILE_AUTHOR = "\uD83D\uDC68\u200D\uD83D\uDCBB";
public static final String SMILE_PEN = "✏️";
public static final String SMILE_NEW_PR = "\uD83C\uDF89";
public static final String SMILE_UPDATE = "\uD83D\uDD04";
public static final String SMILE_SUN = "\uD83D\uDD06";
public static final String SMILE_PIN = "\uD83D\uDCCD";
public static final String HR = "\n -- -- -- -- --\n";
private Message() {
throw new IllegalStateException("Утилитарный класс");
@ -33,8 +36,8 @@ public class Message {
@NonNull
public static String newPullRequest(PullRequest pullRequest) {
return "\uD83C\uDF89 *Новый Pull Request*\n" +
"[" + pullRequest.getName() + "](" + pullRequest.getUrl() + ")\n" +
return SMILE_NEW_PR + " *Новый Pull Request*" + BREAK +
"[" + pullRequest.getName() + "](" + pullRequest.getUrl() + ")" +
HR +
SMILE_AUTHOR + ": " + pullRequest.getAuthor().getLogin() +
TWO_BREAK;
@ -82,7 +85,7 @@ public class Message {
final String createMessage = stringBuilder.toString();
if (!EMPTY.equalsIgnoreCase(createMessage)) {
return Optional.of(
SMILE_PEN + " *Изменения ревьюверов вашего ПР*" + BREAK +
SMILE_PEN + " *Изменения ревьюверов вашего ПР*" +
HR +
"[" + pullRequest.getName() + "](" + pullRequest.getUrl() + ")" + BREAK +
createMessage
@ -99,4 +102,28 @@ public class Message {
SMILE_AUTHOR + ": " + author +
TWO_BREAK;
}
@NonNull
public static String goodMorningStatistic(String userName, List<PullRequest> pullRequests) {
StringBuilder message = new StringBuilder(SMILE_SUN).append(" Доброе утро, ").append(userName).append("!")
.append(HR);
if (!pullRequests.isEmpty()) {
message.append("Сегодня тебя ждет проверка целых ").append(pullRequests.size()).append(" ПР!").append(TWO_BREAK)
.append("Позволь представить, горячая десятка:").append(BREAK);
pullRequests.stream()
.sorted(new UpdateDataComparator())
.limit(10)
.forEach(pullRequest -> message.append(SMILE_PIN)
.append("[").append(pullRequest.getName()).append("](").append(pullRequest.getUrl()).append(")").append(BREAK));
} else {
message.append("Ты либо самый лучший работник, либо тебе не доверяют проверку ПР :D").append(TWO_BREAK)
.append("Поздравляю, у тебя ни одного ПР на проверку!").append(BREAK);
}
return message
.append(BREAK)
.append("Удачной работы!")
.toString();
}
}

View File

@ -0,0 +1,14 @@
package com.tsc.bitbucketbot.utils;
import com.tsc.bitbucketbot.domain.entity.PullRequest;
import java.util.Comparator;
public class UpdateDataComparator implements Comparator<PullRequest> {
@Override
public int compare(PullRequest pullRequest, PullRequest t1) {
return pullRequest.getUpdateDate().compareTo(t1.getUpdateDate());
}
}

View File

@ -15,4 +15,20 @@
</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>