X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=enrichment-coordinator-service%2Fsrc%2Fmain%2Fjava%2Forg%2Foransc%2Fenrichment%2Fcontrollers%2Fconsumer%2FConsumerController.java;h=288421a1375d80ac957b17a2fd89f613467d8a40;hb=5d97a401dc1e26f64ad57daab90f924da9c12c64;hp=7dfaecae9c9ca256633ec10dc0a790bad1b57c1c;hpb=d1d085456c485599f6b8aba87b6d761b29c2ecd4;p=nonrtric.git diff --git a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java index 7dfaecae..288421a1 100644 --- a/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java +++ b/enrichment-coordinator-service/src/main/java/org/oransc/enrichment/controllers/consumer/ConsumerController.java @@ -20,6 +20,7 @@ package org.oransc.enrichment.controllers.consumer; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -32,7 +33,14 @@ import io.swagger.annotations.ApiResponses; import java.util.ArrayList; import java.util.List; +import org.everit.json.schema.Schema; +import org.everit.json.schema.loader.SchemaLoader; +import org.json.JSONObject; +import org.oransc.enrichment.clients.ProducerCallbacks; +import org.oransc.enrichment.configuration.ApplicationConfig; import org.oransc.enrichment.controllers.ErrorResponse; +import org.oransc.enrichment.controllers.VoidResponse; +import org.oransc.enrichment.exceptions.ServiceException; import org.oransc.enrichment.repository.EiJob; import org.oransc.enrichment.repository.EiJobs; import org.oransc.enrichment.repository.EiType; @@ -48,23 +56,31 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; +@SuppressWarnings("java:S3457") // No need to call "toString()" method as formatting and string .. @RestController("ConsumerController") @Api(tags = {ConsumerConsts.CONSUMER_API_NAME}) public class ConsumerController { + @Autowired + ApplicationConfig applicationConfig; + @Autowired private EiJobs eiJobs; @Autowired private EiTypes eiTypes; + @Autowired + ProducerCallbacks producerCallbacks; + private static Gson gson = new GsonBuilder() // .serializeNulls() // .create(); // - @GetMapping(path = ConsumerConsts.A1E_API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Query EI type identifiers", notes = "DETAILS TBD") + @GetMapping(path = ConsumerConsts.API_ROOT + "/eitypes", produces = MediaType.APPLICATION_JSON_VALUE) + @ApiOperation(value = "EI type identifiers", notes = "") @ApiResponses( value = { // @ApiResponse( @@ -77,14 +93,14 @@ public class ConsumerController { ) { List result = new ArrayList<>(); for (EiType eiType : this.eiTypes.getAllEiTypes()) { - result.add(eiType.id()); + result.add(eiType.getId()); } return new ResponseEntity<>(gson.toJson(result), HttpStatus.OK); } - @GetMapping(path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Definitions for an individual EI Type", notes = "Query EI type") + @GetMapping(path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}", produces = MediaType.APPLICATION_JSON_VALUE) + @ApiOperation(value = "Individual EI type", notes = "") @ApiResponses( value = { // @ApiResponse(code = 200, message = "EI type", response = ConsumerEiTypeInfo.class), // @@ -104,9 +120,9 @@ public class ConsumerController { } @GetMapping( - path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs", + path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Query EI job identifiers", notes = "Returns the EI Job identifiers for an EI Type") + @ApiOperation(value = "EI job identifiers", notes = "") @ApiResponses( value = { // @ApiResponse( @@ -138,7 +154,7 @@ public class ConsumerController { } @GetMapping( - path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", + path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", produces = MediaType.APPLICATION_JSON_VALUE) @ApiOperation(value = "Individual EI Job", notes = "") @ApiResponses( @@ -161,7 +177,7 @@ public class ConsumerController { } @GetMapping( - path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}/status", + path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}/status", produces = MediaType.APPLICATION_JSON_VALUE) @ApiOperation(value = "EI Job status", notes = "") @ApiResponses( @@ -184,17 +200,18 @@ public class ConsumerController { } private ConsumerEiJobStatus toEiJobStatus(EiJob job) { + // TODO return new ConsumerEiJobStatus(ConsumerEiJobStatus.OperationalState.ENABLED); } @DeleteMapping( - path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", + path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", produces = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Individual EI Job", notes = "Delete EI job") + @ApiOperation(value = "Individual EI Job", notes = "") @ApiResponses( value = { // - @ApiResponse(code = 200, message = "Not used", response = void.class), - @ApiResponse(code = 204, message = "Job deleted", response = void.class), + @ApiResponse(code = 200, message = "Not used", response = VoidResponse.class), + @ApiResponse(code = 204, message = "Job deleted", response = VoidResponse.class), @ApiResponse( code = 404, message = "Enrichment Information type or job is not found", @@ -203,7 +220,9 @@ public class ConsumerController { @PathVariable("eiTypeId") String eiTypeId, // @PathVariable("eiJobId") String eiJobId) { try { - this.eiJobs.remove(eiJobId); + EiJob job = this.eiJobs.getJob(eiJobId); + this.eiJobs.remove(job); + this.producerCallbacks.notifyProducersJobDeleted(job); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch (Exception e) { return ErrorResponse.create(e, HttpStatus.NOT_FOUND); @@ -211,48 +230,93 @@ public class ConsumerController { } @PutMapping( - path = ConsumerConsts.A1E_API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", // + path = ConsumerConsts.API_ROOT + "/eitypes/{eiTypeId}/eijobs/{eiJobId}", // produces = MediaType.APPLICATION_JSON_VALUE, // consumes = MediaType.APPLICATION_JSON_VALUE) - @ApiOperation(value = "Individual EI Job", notes = "Create or update an EI Job") + @ApiOperation(value = "Individual EI Job", notes = "") @ApiResponses( value = { // - @ApiResponse(code = 201, message = "Job created", response = void.class), // - @ApiResponse(code = 200, message = "Job updated", response = void.class), // , + @ApiResponse(code = 201, message = "Job created", response = VoidResponse.class), // + @ApiResponse(code = 200, message = "Job updated", response = VoidResponse.class), // , @ApiResponse( code = 404, message = "Enrichment Information type is not found", response = ErrorResponse.ErrorInfo.class)}) - public ResponseEntity putIndividualEiJob( // + public Mono> putIndividualEiJob( // @PathVariable("eiTypeId") String eiTypeId, // @PathVariable("eiJobId") String eiJobId, // @RequestBody ConsumerEiJobInfo eiJobInfo) { + + final boolean isNewJob = this.eiJobs.get(eiJobId) == null; + + return validatePutEiJob(eiTypeId, eiJobId, eiJobInfo) // + .flatMap(this::notifyProducersNewJob) // + .doOnNext(newEiJob -> this.eiJobs.put(newEiJob)) // + .flatMap(newEiJob -> Mono.just(new ResponseEntity<>(isNewJob ? HttpStatus.CREATED : HttpStatus.OK))) + .onErrorResume(throwable -> Mono.just(ErrorResponse.create(throwable, HttpStatus.NOT_FOUND))); + } + + private Mono notifyProducersNewJob(EiJob newEiJob) { + return this.producerCallbacks.notifyProducersJobStarted(newEiJob) // + .flatMap(noOfAcceptingProducers -> { + if (noOfAcceptingProducers.intValue() > 0) { + return Mono.just(newEiJob); + } else { + return Mono.error(new ServiceException("Job not accepted by any producers", HttpStatus.CONFLICT)); + } + }); + } + + private Mono validatePutEiJob(String eiTypeId, String eiJobId, ConsumerEiJobInfo eiJobInfo) { try { - this.eiTypes.getType(eiTypeId); // Just to check that the type exists - final boolean newJob = this.eiJobs.get(eiJobId) == null; - this.eiJobs.put(toEiJob(eiJobInfo, eiJobId, eiTypeId)); - return new ResponseEntity<>(newJob ? HttpStatus.CREATED : HttpStatus.OK); + EiType eiType = this.eiTypes.getType(eiTypeId); + validateJsonObjectAgainstSchema(eiType.getJobDataSchema(), eiJobInfo.jobData); + EiJob existingEiJob = this.eiJobs.get(eiJobId); + + if (existingEiJob != null && !existingEiJob.type().getId().equals(eiTypeId)) { + throw new ServiceException("Not allowed to change type for existing EI job", HttpStatus.CONFLICT); + } + return Mono.just(toEiJob(eiJobInfo, eiJobId, eiType)); } catch (Exception e) { - return ErrorResponse.create(e, HttpStatus.NOT_FOUND); + return Mono.error(e); + } + } + + private void validateJsonObjectAgainstSchema(Object schemaObj, Object object) throws ServiceException { + if (schemaObj != null) { // schema is optional for now + try { + ObjectMapper mapper = new ObjectMapper(); + + String schemaAsString = mapper.writeValueAsString(schemaObj); + JSONObject schemaJSON = new JSONObject(schemaAsString); + Schema schema = SchemaLoader.load(schemaJSON); + + String objectAsString = mapper.writeValueAsString(object); + JSONObject json = new JSONObject(objectAsString); + schema.validate(json); + } catch (Exception e) { + throw new ServiceException("Json validation failure " + e.toString(), HttpStatus.CONFLICT); + } } } // Status TBD - private EiJob toEiJob(ConsumerEiJobInfo info, String id, String typeId) { + private EiJob toEiJob(ConsumerEiJobInfo info, String id, EiType type) { return ImmutableEiJob.builder() // .id(id) // - .typeId(typeId) // + .type(type) // .owner(info.owner) // .jobData(info.jobData) // + .targetUri(info.targetUri) // .build(); } private ConsumerEiTypeInfo toEiTypeInfo(EiType t) { - return new ConsumerEiTypeInfo(t.jobDataSchema()); + return new ConsumerEiTypeInfo(t.getJobDataSchema()); } private ConsumerEiJobInfo toEiJobInfo(EiJob s) { - return new ConsumerEiJobInfo(s.jobData(), s.owner()); + return new ConsumerEiJobInfo(s.jobData(), s.owner(), s.targetUri()); } }