Skip to content

Commit

Permalink
Feature: discord.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateusz Czeladka committed Sep 14, 2023
1 parent 3f8fa56 commit a6646da
Show file tree
Hide file tree
Showing 13 changed files with 309 additions and 124 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package org.cardano.foundation.voting.domain.discord;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.cardano.foundation.voting.domain.VerificationStatus;

@Builder
@Getter
@AllArgsConstructor
public class DiscordStartVerificationResponse {

String eventId;
String discordIdHash;
VerificationStatus status;
private String eventId;

private String discordIdHash;

private VerificationStatus status;

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,36 @@
import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.domain.VerificationStatus;

import javax.annotation.Nullable;
import java.time.LocalDateTime;
import java.util.Optional;

@Entity
@Table(name = "discord_user_verification")
@Slf4j
@NoArgsConstructor
@SuperBuilder
@Getter
@Setter
@AllArgsConstructor
public class DiscordUserVerification extends AbstractTimestampEntity {

@Id
@Column(name = "discord_id_hash", nullable = false)
@Getter
@Setter
private String discordIdHash;

@Column(name = "event_id", nullable = false)
@Getter
@Setter
private String eventId;

@Column(name = "stake_address", nullable = false)
@Column(name = "stake_address")
@Nullable
private String stakeAddress;

@Column(name = "secret_code", nullable = false)
@Getter
@Setter
private String secretCode;

@Column(name = "status", nullable = false)
Expand All @@ -46,6 +53,14 @@ public class DiscordUserVerification extends AbstractTimestampEntity {
@Setter
private LocalDateTime expiresAt;

public Optional<String> getStakeAddress() {
return Optional.ofNullable(stakeAddress);
}

public void setStakeAddress(Optional<String> stakeAddress) {
this.stakeAddress = stakeAddress.orElse(null);
}

@Override
public String toString() {
return "DiscordUserVerification{" +
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.cardano.foundation.voting.jobs;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.client.ChainFollowerClient;
import org.cardano.foundation.voting.service.discord.DiscordUserVerificationService;
import org.cardano.foundation.voting.service.sms.SMSUserVerificationService;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
@Slf4j
@RequiredArgsConstructor
public class CleanupAllForFinishedEventsJob implements Runnable {

private final ChainFollowerClient chainFollowerClient;

private final SMSUserVerificationService smsUserVerificationService;

private final DiscordUserVerificationService discordUserVerificationService;

@Scheduled(cron = "${finished.verifications.cleanup.job.cron}")
public void run() {
log.info("Cleaning up verified phones for event...");

var allEventsE = chainFollowerClient.findAllEvents();

if (allEventsE.isEmpty()) {
log.warn("No events found in ledger follower, skipping cleanup job.");
return;
}

var allEvents = allEventsE.get();

allEvents.forEach(eventSummary -> {
if (eventSummary.finished()) {
log.info("Event:{} is finished, removing all sms verifications...", eventSummary.id());

smsUserVerificationService.findAllForEvent(eventSummary.id()).forEach(userVerification -> {
log.info("Removing historical user sms verification... since eventId:{} is finished.", eventSummary.id());
smsUserVerificationService.removeUserVerification(userVerification);
});

log.info("Event:{} is finished, removing all discord verifications...", eventSummary.id());
discordUserVerificationService.findAllForEvent(eventSummary.id()).forEach(userVerification -> {
log.info("Removing historical user discord verification... since eventId:{} is finished.", eventSummary.id());
discordUserVerificationService.removeUserVerification(userVerification);
});
}
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.client.ChainFollowerClient;
import org.cardano.foundation.voting.service.discord.DiscordUserVerificationService;
import org.cardano.foundation.voting.service.sms.SMSUserVerificationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -13,14 +14,17 @@

@Service
@Slf4j
public class SMSPendingVerificationPhoneCleanupJob implements Runnable {
public class PendingVerificationCleanupJob implements Runnable {

@Autowired
private ChainFollowerClient chainFollowerClient;

@Autowired
private SMSUserVerificationService smsUserVerificationService;

@Autowired
private DiscordUserVerificationService discordUserVerificationService;

@Autowired
private Clock clock;

Expand All @@ -43,6 +47,7 @@ public void run() {
allEvents.forEach(eventSummary -> {
var id = eventSummary.id();

log.info("Cleaning up pending phone verifications for event: {}", id);
smsUserVerificationService.findAllPending(id).forEach(userVerification -> {
var now = LocalDateTime.now(clock);

Expand All @@ -56,6 +61,19 @@ public void run() {
}
});

log.info("Cleaning up pending discord verifications for event: {}", id);
discordUserVerificationService.findAllPending(id).forEach(userVerification -> {
var now = LocalDateTime.now(clock);

boolean expiredVerification = now.isAfter(userVerification.getCreatedAt()
.plusHours(pendingVerificationPhoneExpirationTimeHours));

if (expiredVerification) {
log.info("Deleting expired pending user verification: {}", userVerification);
discordUserVerificationService.removeUserVerification(userVerification);
}
});

});
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
Expand All @@ -18,15 +19,20 @@ Optional<DiscordUserVerification> findCompletedVerification(@Param("eventId") St

@Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.status = 'VERIFIED' AND uv.eventId = :eventId AND uv.discordIdHash = :discordIdHash")
Optional<DiscordUserVerification> findCompletedVerificationBasedOnDiscordUserHash(@Param("eventId") String eventId,
@Param("discordIdHash") String discordIdHash);
@Param("discordIdHash") String discordIdHash
);

@Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.status = 'PENDING'" +
" AND uv.eventId = :eventId" +
" AND uv.discordIdHash = :discordIdHash" +
" AND uv.secretCode = :secretCode")
Optional<DiscordUserVerification> findPendingVerification(@Param("eventId") String eventId,
@Param("discordIdHash") String discordIdHash,
@Param("secretCode") String secretCode
" AND uv.discordIdHash = :discordIdHash")
Optional<DiscordUserVerification> findPendingVerificationBasedOnDiscordUserHash(@Param("eventId") String eventId,
@Param("discordIdHash") String discordIdHash
);

@Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.eventId = :eventId")
List<DiscordUserVerification> findAllForEvent(String eventId);

@Query("SELECT uv FROM DiscordUserVerification uv WHERE uv.eventId = :eventId AND uv.status = 'PENDING'")
List<DiscordUserVerification> findAllPending(String eventId);

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package org.cardano.foundation.voting.resource;

import io.micrometer.core.annotation.Timed;
import io.vavr.control.Either;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.cardano.foundation.voting.domain.IsVerifiedRequest;
import org.cardano.foundation.voting.domain.IsVerifiedResponse;
import org.cardano.foundation.voting.service.discord.DiscordUserVerificationService;
import org.cardano.foundation.voting.service.sms.SMSUserVerificationService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zalando.problem.Problem;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.springframework.web.bind.annotation.RequestMethod.GET;

@RestController
Expand All @@ -24,19 +31,32 @@ public class UserVerificationResource {

@RequestMapping(value = "/verified/{eventId}/{stakeAddress}", method = GET, produces = "application/json")
@Timed(value = "resource.isVerified", histogram = true)
public ResponseEntity<?> isVerified(@PathVariable("eventId") String eventId, @PathVariable("stakeAddress") String stakeAddress) {
public ResponseEntity<?> isVerified(@PathVariable("eventId") String eventId, @PathVariable("stakeAddress") String stakeAddress) throws ExecutionException, InterruptedException {
var isVerifiedRequest = new IsVerifiedRequest(stakeAddress, eventId);

log.info("Received isVerified request: {}", isVerifiedRequest);

// TODO fork join to sms and discord services and check is verified for both in parallel
CompletableFuture<Either<Problem, IsVerifiedResponse>> smsVerificationFuture = CompletableFuture.supplyAsync(() -> {
return smsUserVerificationService.isVerified(isVerifiedRequest);
});

CompletableFuture<Either<Problem, IsVerifiedResponse>> discordVerificationFuture = CompletableFuture.supplyAsync(() -> {
return discordUserVerificationService.isVerified(isVerifiedRequest);
});

var isVerified = CompletableFuture.anyOf(smsVerificationFuture, discordVerificationFuture);

var isVerifiedResponseE = (Either<Problem, IsVerifiedResponse>) isVerified
.orTimeout(30, SECONDS)
.get();

return smsUserVerificationService.isVerified(isVerifiedRequest)
.fold(problem -> ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem),
isVerifiedResponse -> {
return ResponseEntity.ok().body(isVerifiedResponse);
}
);
return isVerifiedResponseE.fold(problem -> {
return ResponseEntity.status(problem.getStatus().getStatusCode()).body(problem);
},
isVerifiedResponse -> {
return ResponseEntity.ok().body(isVerifiedResponse);
}
);
}

}
Loading

0 comments on commit a6646da

Please sign in to comment.