Skip to content

Commit ff2677f

Browse files
Hotfix/expired credential status schedule task (#54)
* implemented scheduler * tests * updated impl and tests with Procedure * build gradle and changelog * logs * changelog * task updates updated_at * fix condition * changed cron * fix action? * PR fixes * PR fixes * changed test cron * fix PR
1 parent a81e645 commit ff2677f

File tree

6 files changed

+150
-2
lines changed

6 files changed

+150
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [v1.2.1](https://github.com/in2workspace/in2-issuer-api/releases/tag/v1.1.4)
7+
## [v1.2.2](https://github.com/in2workspace/in2-issuer-api/releases/tag/v1.2.2)
8+
### Added
9+
- Add scheduled task to set EXPIRED status to credentials that have expired.
10+
11+
## [v1.2.1](https://github.com/in2workspace/in2-issuer-api/releases/tag/v1.2.1)
812
### Added
913
- Add support for requesting a fresh QR code if the previous one has expired or was an error during the proccess of
1014

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ plugins {
1111
}
1212

1313
group = 'es.in2'
14-
version = '1.2.1'
14+
version = '1.2.2'
1515

1616
java {
1717
sourceCompatibility = '17'

src/main/java/es/in2/issuer/IssuerApiApplication.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import org.springframework.boot.autoconfigure.SpringBootApplication;
88
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
99
import org.springframework.context.annotation.Bean;
10+
import org.springframework.scheduling.annotation.EnableScheduling;
1011

1112
@SpringBootApplication
1213
@ConfigurationPropertiesScan
14+
@EnableScheduling
1315
public class IssuerApiApplication {
1416

1517
private static final ObjectMapper OBJECT_MAPPER =
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package es.in2.issuer.application.scheduler;
2+
import reactor.core.publisher.Mono;
3+
4+
public interface CredentialExpirationScheduler {
5+
Mono<Void> checkAndExpireCredentials();
6+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package es.in2.issuer.application.scheduler;
2+
3+
import es.in2.issuer.domain.model.entities.CredentialProcedure;
4+
import es.in2.issuer.domain.model.enums.CredentialStatus;
5+
import es.in2.issuer.infrastructure.repository.CredentialProcedureRepository;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.springframework.scheduling.annotation.EnableScheduling;
9+
import org.springframework.scheduling.annotation.Scheduled;
10+
import org.springframework.stereotype.Component;
11+
import reactor.core.publisher.Mono;
12+
import java.sql.Timestamp;
13+
import java.time.Instant;
14+
15+
@Component
16+
@Slf4j
17+
@EnableScheduling
18+
@RequiredArgsConstructor
19+
public class CredentialExpirationSchedulerImpl implements CredentialExpirationScheduler {
20+
21+
private final CredentialProcedureRepository credentialProcedureRepository;
22+
23+
@Override
24+
@Scheduled(cron = "0 0 1 * * ?") //Every day at 1:00 AM
25+
public Mono<Void> checkAndExpireCredentials() {
26+
log.info("Scheduled Task - Executing checkAndExpireCredentials at: {}", Instant.now());
27+
28+
return credentialProcedureRepository.findAll()
29+
.flatMap(credential -> isExpired(credential)
30+
.filter(Boolean::booleanValue)
31+
.flatMap(expired -> expireCredential(credential)))
32+
.then();
33+
}
34+
35+
private Mono<Boolean> isExpired(CredentialProcedure credentialProcedure) {
36+
return Mono.justOrEmpty(credentialProcedure.getValidUntil())
37+
.map(validUntil -> validUntil.toInstant().isBefore(Instant.now()))
38+
.defaultIfEmpty(false);
39+
}
40+
41+
Mono<CredentialProcedure> expireCredential(CredentialProcedure credentialProcedure) {
42+
if(credentialProcedure.getCredentialStatus() != CredentialStatus.EXPIRED) {
43+
credentialProcedure.setCredentialStatus(CredentialStatus.EXPIRED);
44+
credentialProcedure.setUpdatedAt(Timestamp.from(Instant.now()));
45+
log.info("Expiring credential with ID: {} - New state: {}",
46+
credentialProcedure.getCredentialId(),
47+
credentialProcedure.getCredentialStatus());
48+
return credentialProcedureRepository.save(credentialProcedure);
49+
}
50+
return Mono.empty();
51+
}
52+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package es.in2.issuer.application.scheduler;
2+
3+
import es.in2.issuer.domain.model.entities.CredentialProcedure;
4+
import es.in2.issuer.domain.model.enums.CredentialStatus;
5+
import es.in2.issuer.infrastructure.repository.CredentialProcedureRepository;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.Test;
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
import static org.junit.jupiter.api.Assertions.assertNull;
10+
import org.mockito.InjectMocks;
11+
import org.mockito.Mock;
12+
import org.mockito.MockitoAnnotations;
13+
import reactor.core.publisher.Flux;
14+
import reactor.core.publisher.Mono;
15+
import reactor.test.StepVerifier;
16+
17+
import com.fasterxml.jackson.databind.ObjectMapper;
18+
19+
import java.sql.Timestamp;
20+
import java.time.Instant;
21+
22+
import static org.mockito.Mockito.*;
23+
24+
class CredentialExpirationSchedulerImplTest {
25+
26+
@Mock
27+
private CredentialProcedureRepository credentialProcedureRepository;
28+
29+
@Mock
30+
private ObjectMapper objectMapper;
31+
32+
@InjectMocks
33+
private CredentialExpirationSchedulerImpl scheduler;
34+
35+
@BeforeEach
36+
void setUp() {
37+
MockitoAnnotations.openMocks(this);
38+
}
39+
40+
@Test
41+
void shouldExpireCredentialsWhenValidUntilHasPassed() {
42+
CredentialProcedure credential = new CredentialProcedure();
43+
credential.setCredentialId(java.util.UUID.randomUUID());
44+
credential.setCredentialStatus(CredentialStatus.VALID);
45+
credential.setValidUntil(Timestamp.from(Instant.now().minusSeconds(60)));
46+
47+
when(credentialProcedureRepository.findAll()).thenReturn(Flux.just(credential));
48+
when(credentialProcedureRepository.save(any(CredentialProcedure.class)))
49+
.thenAnswer(invocation -> Mono.just(invocation.getArgument(0)));
50+
51+
StepVerifier.create(scheduler.checkAndExpireCredentials())
52+
.expectSubscription()
53+
.verifyComplete();
54+
55+
verify(credentialProcedureRepository, atLeastOnce()).save(argThat(updatedCredential -> {
56+
boolean statusCorrect = updatedCredential.getCredentialStatus() == CredentialStatus.EXPIRED;
57+
boolean updatedAtNotNull = updatedCredential.getUpdatedAt() != null;
58+
boolean updatedAtRecent = updatedCredential.getUpdatedAt().toInstant().isAfter(Instant.now().minusSeconds(10));
59+
60+
return statusCorrect && updatedAtNotNull && updatedAtRecent;
61+
}));
62+
}
63+
64+
@Test
65+
void shouldNotExpireCredentialsIfValidUntilHasNotPassed() {
66+
CredentialProcedure credential = new CredentialProcedure();
67+
credential.setCredentialId(java.util.UUID.randomUUID());
68+
credential.setCredentialStatus(CredentialStatus.VALID);
69+
credential.setValidUntil(Timestamp.from(Instant.now().plusSeconds(60)));
70+
71+
when(credentialProcedureRepository.findAll()).thenReturn(Flux.just(credential));
72+
when(credentialProcedureRepository.save(any(CredentialProcedure.class)))
73+
.thenAnswer(invocation -> Mono.just(invocation.getArgument(0)));
74+
75+
StepVerifier.create(scheduler.checkAndExpireCredentials())
76+
.expectSubscription()
77+
.verifyComplete();
78+
79+
verify(credentialProcedureRepository, never()).save(any(CredentialProcedure.class));
80+
81+
assertEquals(CredentialStatus.VALID, credential.getCredentialStatus());
82+
assertNull(credential.getUpdatedAt());
83+
}
84+
}

0 commit comments

Comments
 (0)