-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from DiSSCo/feature/error-recovery-controller
Add recovery endpoint
- Loading branch information
Showing
19 changed files
with
744 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
src/main/java/eu/dissco/core/datacitepublisher/controller/RecoveryController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package eu.dissco.core.datacitepublisher.controller; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import eu.dissco.core.datacitepublisher.domain.RecoveryEvent; | ||
import eu.dissco.core.datacitepublisher.exceptions.DataCiteApiException; | ||
import eu.dissco.core.datacitepublisher.exceptions.HandleResolutionException; | ||
import eu.dissco.core.datacitepublisher.service.RecoveryService; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequestMapping("/api/v1/datacite-recovery") | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class RecoveryController { | ||
|
||
private final RecoveryService recoveryService; | ||
|
||
@PostMapping("") | ||
public ResponseEntity<Void> recoverPids(@RequestBody RecoveryEvent event) | ||
throws HandleResolutionException, DataCiteApiException, JsonProcessingException { | ||
recoveryService.recoverDataciteDois(event); | ||
return ResponseEntity.ok(null); | ||
} | ||
|
||
|
||
} |
30 changes: 30 additions & 0 deletions
30
src/main/java/eu/dissco/core/datacitepublisher/domain/FdoType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package eu.dissco.core.datacitepublisher.domain; | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
import lombok.ToString; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
@Slf4j | ||
public enum FdoType { | ||
@JsonProperty("https://hdl.handle.net/21.T11148/894b1e6cad57e921764e") DIGITAL_SPECIMEN, | ||
@JsonProperty("https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115") MEDIA_OBJECT; | ||
|
||
public static FdoType fromString(String type) { | ||
if ("https://hdl.handle.net/21.T11148/894b1e6cad57e921764e".equals(type)) { | ||
return DIGITAL_SPECIMEN; | ||
} | ||
if ("https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115".equals(type)) { | ||
return MEDIA_OBJECT; | ||
} | ||
log.error("Invalid DOI type: {}", type); | ||
throw new IllegalStateException(); | ||
} | ||
|
||
@Override | ||
public String toString(){ | ||
if (this.equals(DIGITAL_SPECIMEN)) return "https://hdl.handle.net/21.T11148/894b1e6cad57e921764e"; | ||
return "https://hdl.handle.net/21.T11148/bbad8c4e101e8af01115"; | ||
} | ||
|
||
|
||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/eu/dissco/core/datacitepublisher/domain/RecoveryEvent.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package eu.dissco.core.datacitepublisher.domain; | ||
|
||
import java.util.List; | ||
|
||
public record RecoveryEvent( | ||
List<String> handles, | ||
EventType eventType | ||
) { | ||
|
||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/eu/dissco/core/datacitepublisher/exceptions/HandleResolutionException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package eu.dissco.core.datacitepublisher.exceptions; | ||
|
||
public class HandleResolutionException extends Exception { | ||
|
||
public HandleResolutionException(){ | ||
super(); | ||
} | ||
|
||
} |
20 changes: 20 additions & 0 deletions
20
src/main/java/eu/dissco/core/datacitepublisher/properties/HandleConnectionProperties.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package eu.dissco.core.datacitepublisher.properties; | ||
|
||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.Data; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.validation.annotation.Validated; | ||
|
||
@Data | ||
@Validated | ||
@ConfigurationProperties("handle") | ||
public class HandleConnectionProperties { | ||
|
||
@NotBlank | ||
private String endpoint = "https://sandbox.dissco.tech/handle-manager/api/v1/pids/records"; | ||
|
||
@NotNull | ||
private int maxHandles = 100; | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
src/main/java/eu/dissco/core/datacitepublisher/security/JwtAuthConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package eu.dissco.core.datacitepublisher.security; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
import java.util.Collection; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
import org.springframework.core.convert.converter.Converter; | ||
import org.springframework.security.authentication.AbstractAuthenticationToken; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.oauth2.jwt.Jwt; | ||
import org.springframework.security.oauth2.jwt.JwtClaimNames; | ||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; | ||
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class JwtAuthConverter implements Converter<Jwt, AbstractAuthenticationToken> { | ||
private final JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); | ||
|
||
@Override | ||
public AbstractAuthenticationToken convert(@NotNull Jwt jwt) { | ||
Collection<GrantedAuthority> authorities = | ||
converterToStream(jwt).collect(Collectors.toSet()); | ||
return new JwtAuthenticationToken(jwt, authorities, getPrincipalClaimName(jwt)); | ||
} | ||
|
||
private Stream<GrantedAuthority> converterToStream(Jwt jwt){ | ||
return Optional.of(jwtGrantedAuthoritiesConverter.convert(jwt)).stream() | ||
.flatMap(Collection::stream); | ||
} | ||
|
||
private String getPrincipalClaimName(Jwt jwt) { | ||
return jwt.getClaim(JwtClaimNames.SUB); | ||
} | ||
|
||
} |
19 changes: 19 additions & 0 deletions
19
src/main/java/eu/dissco/core/datacitepublisher/security/MethodSecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package eu.dissco.core.datacitepublisher.security; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; | ||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; | ||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; | ||
|
||
@Configuration | ||
@EnableMethodSecurity | ||
public class MethodSecurityConfig { | ||
|
||
@Bean | ||
protected MethodSecurityExpressionHandler createExpressionHandler() { | ||
return new DefaultMethodSecurityExpressionHandler(); | ||
} | ||
|
||
} | ||
|
34 changes: 34 additions & 0 deletions
34
src/main/java/eu/dissco/core/datacitepublisher/security/WebSecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package eu.dissco.core.datacitepublisher.security; | ||
|
||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | ||
import org.springframework.security.config.http.SessionCreationPolicy; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
|
||
@RequiredArgsConstructor | ||
@Configuration | ||
@EnableWebSecurity | ||
public class WebSecurityConfig { | ||
|
||
private final JwtAuthConverter jwtAuthConverter; | ||
|
||
@Bean | ||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { | ||
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests | ||
.anyRequest() | ||
.hasRole("orchestration-admin")); | ||
|
||
http.oauth2ResourceServer(jwtoauth2ResourceServer -> jwtoauth2ResourceServer.jwt(( | ||
jwt -> jwt.jwtAuthenticationConverter(jwtAuthConverter) | ||
))); | ||
|
||
http.sessionManagement(sessionManagement -> sessionManagement | ||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); | ||
return http.build(); | ||
} | ||
|
||
} |
84 changes: 84 additions & 0 deletions
84
src/main/java/eu/dissco/core/datacitepublisher/service/RecoveryService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package eu.dissco.core.datacitepublisher.service; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import eu.dissco.core.datacitepublisher.domain.DigitalSpecimenEvent; | ||
import eu.dissco.core.datacitepublisher.domain.EventType; | ||
import eu.dissco.core.datacitepublisher.domain.FdoType; | ||
import eu.dissco.core.datacitepublisher.domain.MediaObjectEvent; | ||
import eu.dissco.core.datacitepublisher.domain.RecoveryEvent; | ||
import eu.dissco.core.datacitepublisher.exceptions.DataCiteApiException; | ||
import eu.dissco.core.datacitepublisher.exceptions.HandleResolutionException; | ||
import eu.dissco.core.datacitepublisher.properties.HandleConnectionProperties; | ||
import eu.dissco.core.datacitepublisher.schemas.DigitalSpecimen; | ||
import eu.dissco.core.datacitepublisher.schemas.MediaObject; | ||
import eu.dissco.core.datacitepublisher.web.HandleClient; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class RecoveryService { | ||
|
||
private final HandleClient handleClient; | ||
private final DataCitePublisherService dataCitePublisherService; | ||
private final ObjectMapper mapper; | ||
private final HandleConnectionProperties handleConnectionProperties; | ||
|
||
public void recoverDataciteDois(RecoveryEvent event) | ||
throws HandleResolutionException, JsonProcessingException, DataCiteApiException { | ||
int handlesProcessed = 0; | ||
while (handlesProcessed < event.handles().size()){ | ||
int upperIndex = getUpperIndex(handlesProcessed, event.handles().size()); | ||
var handles = event.handles().subList(handlesProcessed, upperIndex); | ||
processResolvedHandles(handles, event.eventType()); | ||
handlesProcessed = upperIndex; | ||
} | ||
} | ||
|
||
private int getUpperIndex(int handlesProcessed, int totalHandles) { | ||
if (handlesProcessed + handleConnectionProperties.getMaxHandles() > totalHandles) { | ||
return totalHandles; | ||
} | ||
return handlesProcessed + handleConnectionProperties.getMaxHandles(); | ||
} | ||
|
||
private void processResolvedHandles(List<String> handles, EventType eventType) | ||
throws HandleResolutionException, DataCiteApiException, JsonProcessingException { | ||
|
||
var handleResolutionResponse = handleClient.resolveHandles(handles); | ||
if (handleResolutionResponse.get("data") != null && handleResolutionResponse.get("data").isArray()) { | ||
var dataNodes = handleResolutionResponse.get("data"); | ||
for (var pidRecordJson : dataNodes) { | ||
var type = FdoType.fromString(pidRecordJson.get("type").asText()); | ||
if (type.equals(FdoType.DIGITAL_SPECIMEN)) { | ||
recoverDigitalSpecimen(pidRecordJson.get("attributes"), eventType); | ||
} else { | ||
recoverMediaObject(pidRecordJson.get("attributes"), eventType); | ||
} | ||
} | ||
} else { | ||
log.error("Unexpected response from handle api: {}", handleResolutionResponse); | ||
throw new HandleResolutionException(); | ||
} | ||
} | ||
|
||
|
||
|
||
private void recoverDigitalSpecimen(JsonNode pidRecordAttributes, EventType eventType) | ||
throws JsonProcessingException, DataCiteApiException { | ||
var digitalSpecimen = mapper.treeToValue(pidRecordAttributes, DigitalSpecimen.class); | ||
dataCitePublisherService.handleMessages(new DigitalSpecimenEvent(digitalSpecimen, eventType)); | ||
} | ||
|
||
private void recoverMediaObject(JsonNode pidRecordAttributes, EventType eventType) | ||
throws DataCiteApiException, JsonProcessingException { | ||
var mediaObject = mapper.treeToValue(pidRecordAttributes, MediaObject.class); | ||
dataCitePublisherService.handleMessages(new MediaObjectEvent(mediaObject, eventType)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.