Skip to content

Commit

Permalink
feat: started DOIP v2.0 over HTTP implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Pfeil committed Aug 17, 2023
1 parent 0bbf95a commit bfcfe5c
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 0 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ dependencies {
implementation('org.apache.httpcomponents:httpclient-cache:4.5.14')

implementation("net.handle:handle-client:9.3.1")
implementation("net.dona.doip:doip-sdk:2.1.0")

testImplementation(platform('org.junit:junit-bom:5.10.0'))
testImplementation('org.junit.jupiter:junit-jupiter')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Adapted from https://git.rwth-aachen.de/nfdi4ing/s-3/s-3-3/metadatahub/-/blob/main/src/main/java/edu/kit/metadatahub/doip/rest/Operations.java
*
* License: Apache 2.0
*/

package edu.kit.datamanager.pit.web.doip;

import net.dona.doip.DoipConstants;

/**
* Define valid operations for REST interface of DOIP.
*/
public enum DoipOperationId {
// Basic operations
OP_HELLO(DoipConstants.OP_HELLO),
OP_CREATE(DoipConstants.OP_CREATE),
OP_RETRIEVE(DoipConstants.OP_RETRIEVE),
OP_UPDATE(DoipConstants.OP_UPDATE),
OP_DELETE(DoipConstants.OP_DELETE),
OP_SEARCH(DoipConstants.OP_SEARCH),
OP_LIST(DoipConstants.OP_LIST_OPERATIONS),
// Extended operations
OP_VALIDATE("dev/Validation");

private final String value;

DoipOperationId(String v) {
value = v;
}

public String value() {
return value;
}

public static DoipOperationId fromValue(String v) {
for (DoipOperationId c : DoipOperationId.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}

}
179 changes: 179 additions & 0 deletions src/main/java/edu/kit/datamanager/pit/web/doip/DoipOverHttp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/**
* A lot of code re-used from metadataHub, Apache 2.0 License.
*
* Original Code: https://git.rwth-aachen.de/nfdi4ing/s-3/s-3-3/metadatahub/-/blob/main/src/main/java/edu/kit/metadatahub/rest/controller/Rest4DoipController.java
*/

package edu.kit.datamanager.pit.web.doip;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

import org.apache.commons.lang3.NotImplementedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import edu.kit.datamanager.pit.pitservice.ITypingService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

/**
* REST controller for DOIP over HTTP.
*/
@RestController
@CrossOrigin(origins = "*", allowedHeaders = "*")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The request was processed successfully."),
@ApiResponse(responseCode = "400", description = "There was something wrong with the structure or content of the request"),
@ApiResponse(responseCode = "401", description = "The client must authenticate to perform the attempted operation"),
@ApiResponse(responseCode = "403", description = "The client was not permitted to perform the attempted operation"),
@ApiResponse(responseCode = "404", description = "The requested digital object could not be found"),
@ApiResponse(responseCode = "409", description = "There was a conflict preventing the request from being executed"),
@ApiResponse(responseCode = "500", description = "There was an internal server error") })
public class DoipOverHttp {

private static final Logger LOGGER = LoggerFactory.getLogger(DoipOverHttp.class);

private final String SELF_IDENTIFIER_FALLBACK = "self";
private final Collection<DoipOperationId> SERVICE_OPERATIONS = List.of(
//DoipOperationId.OP_HELLO,
//DoipOperationId.OP_CREATE,
//DoipOperationId.OP_RETRIEVE,
//DoipOperationId.OP_SEARCH,
DoipOperationId.OP_LIST
);
private final Collection<DoipOperationId> FAIRDO_OPERATIONS = List.of(
//DoipOperationId.OP_UPDATE,
//DoipOperationId.OP_DELETE,
DoipOperationId.OP_LIST
//DoipOperationId.OP_VALIDATE
);

@Autowired
ITypingService pit;

public DoipResponse op_server_create(
final DoipOperationId operationId,
final String targetId,
final String clientId,
final JsonNode attributes,
final DoipRequest body
) {
throw new NotImplementedException();
}

/**
* DOIP Entpoint. Basically executes targetId.operationId(attributes, body, clientId).
*/
@Operation(summary = "DOIPv2 over HTTP.", description = "DOIPv2 via HTTP as defined by Cordra, as far as applicable.")
@PostMapping(value = { "/doip" }, consumes = { MediaType.ALL_VALUE })
@ResponseBody
public ResponseEntity<DoipResponse> postDoipOperation(
@Parameter(description = "The operationId.", required = true)
@RequestParam(value = "operationId")
final DoipOperationId operationId,

@Parameter(description = "The targetId.", required = true)
@RequestParam(value = "targetId")
final String targetId,

@Parameter(description = "the identifier of the caller", required = true)
@RequestParam(value = "clientId")
final String clientId,

@Parameter(description = "arbitrary JSON to inform the operation", required = true)
@RequestParam(value = "attributes")
final JsonNode attributes,

@Parameter(description = "Json representation of the Digital Object.", required = false)
@RequestBody(required = false)
final DoipRequest body
) {
// Authentication: is done by security layer (keycloak / JWT)

boolean isServiceTarget = Objects.equals(targetId, SELF_IDENTIFIER_FALLBACK) /* TODO or a real identifier configured for this service? */;

if (isServiceTarget) {
switch (operationId) {
case OP_LIST:
return operation_service_list(targetId, operationId, body, attributes, clientId);
//case OP_HELLO:
// break;
//case OP_CREATE:
// break;
//case OP_RETRIEVE:
// break;
//case OP_SEARCH:
// break;
default:
DoipResponse r = new DoipResponse(DoipStatus.REQUEST_INVALID);
return new ResponseEntity<>(r, r.getDoipStatus().getHttpStatus());
}
} else {
// check if identifier is registered
if (pit.isIdentifierRegistered(targetId)) {
switch (operationId) {
case OP_LIST:
return operation_fairdo_list(targetId, operationId, body, attributes, clientId);
//case OP_UPDATE:
// break;
//case OP_DELETE:
// break;
//case OP_VALIDATE:
// break;
default:
DoipResponse r = new DoipResponse(DoipStatus.REQUEST_INVALID);
return new ResponseEntity<>(r, r.getDoipStatus().getHttpStatus());
}
} else {
DoipResponse r = new DoipResponse(DoipStatus.DIGITAL_OBJECT_NOT_FOUND);
return new ResponseEntity<>(r, r.getDoipStatus().getHttpStatus());
}
// if not, return error
}
}

private ResponseEntity<DoipResponse> operation_fairdo_list(
String targetId,
DoipOperationId operationId,
DoipRequest body,
JsonNode attributes,
String clientId
){
ObjectMapper mapper = new ObjectMapper();
DoipResponse r = new DoipResponse(DoipStatus.OPERATION_SUCCESS);
// TODO add other known operations
r.setOutput(mapper.valueToTree(FAIRDO_OPERATIONS));
return new ResponseEntity<>(r, r.getDoipStatus().getHttpStatus());
}

private ResponseEntity<DoipResponse> operation_service_list(
String targetId,
DoipOperationId operationId,
DoipRequest body,
JsonNode attributes,
String clientId
) {
ObjectMapper mapper = new ObjectMapper();
DoipResponse r = new DoipResponse(DoipStatus.OPERATION_SUCCESS);
r.setOutput(mapper.valueToTree(SERVICE_OPERATIONS));
return new ResponseEntity<>(r, r.getDoipStatus().getHttpStatus());
}

}
84 changes: 84 additions & 0 deletions src/main/java/edu/kit/datamanager/pit/web/doip/DoipRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Adapted from: https://git.rwth-aachen.de/nfdi4ing/s-3/s-3-3/metadatahub/-/blob/main/src/main/java/edu/kit/metadatahub/doip/rest/RestDoip.java#L39
*
* License: Apache 2.0
*/
package edu.kit.datamanager.pit.web.doip;

import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import edu.kit.datamanager.pit.domain.SimplePair;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"id",
"clientId",
"attributes"
})
public class DoipRequest {

@JsonProperty("id")
private String id;
@JsonProperty("clientId")
private String clientId;
@JsonProperty("targetId")
private String targetId;
@JsonProperty("token")
private String token;
@JsonProperty("attributes")
private List<SimplePair> attributes = new ArrayList<>();

@JsonProperty("id")
public String getId() {
return id;
}

@JsonProperty("id")
public void setId(String id) {
this.id = id;
}

@JsonProperty("clientId")
public String getClientId() {
return clientId;
}

@JsonProperty("clientId")
public void setClientId(String clientId) {
this.clientId = clientId;
}

@JsonProperty("targetId")
public String getTargetId() {
return targetId;
}

@JsonProperty("targetId")
public void setTargetId(String targetId) {
this.targetId = targetId;
}

@JsonProperty("token")
public String getToken() {
return token;
}

@JsonProperty("token")
public void setToken(String token) {
this.token = token;
}

@JsonProperty("attributes")
public List<SimplePair> getAttributes() {
return attributes;
}

@JsonProperty("attributes")
public void setAttributes(List<SimplePair> attributes) {
this.attributes = attributes;
}
}
Loading

0 comments on commit bfcfe5c

Please sign in to comment.