Search in sources :

Example 1 with CompositionResponseData

use of org.ehrbase.response.openehr.CompositionResponseData in project ehrbase by ehrbase.

the class OpenehrCompositionController method updateComposition.

@PutMapping("/{ehr_id}/composition/{versioned_object_uid}")
// checkAbacPre /-Post attributes (type, subject, payload, content type)
@PreAuthorize("checkAbacPre(@openehrCompositionController.COMPOSITION, " + "@ehrService.getSubjectExtRef(#ehrIdString), #composition, #contentType)")
@Override
public ResponseEntity updateComposition(String openehrVersion, @RequestHeader(value = "openEHR-AUDIT_DETAILS", required = false) String openehrAuditDetails, @RequestHeader(value = CONTENT_TYPE, required = false) String contentType, @RequestHeader(value = ACCEPT, required = false) String accept, @RequestHeader(value = PREFER, required = false) String prefer, @RequestHeader(value = IF_MATCH) String ifMatch, @PathVariable(value = "ehr_id") String ehrIdString, @PathVariable(value = "versioned_object_uid") String versionedObjectUidString, @RequestBody String composition, HttpServletRequest request) {
    UUID ehrId = getEhrUuid(ehrIdString);
    UUID versionedObjectUid = getCompositionVersionedObjectUidString(versionedObjectUidString);
    CompositionFormat compositionFormat = extractCompositionFormat(contentType);
    // check if composition ID path variable is valid
    compositionService.exists(versionedObjectUid);
    // If the If-Match is not the latest latest existing version, throw error
    if (!((versionedObjectUid + "::" + compositionService.getServerConfig().getNodename() + "::" + compositionService.getLastVersionNumber(extractVersionedObjectUidFromVersionUid(versionedObjectUid.toString()))).equals(ifMatch))) {
        throw new PreconditionFailedException("If-Match header does not match latest existing version");
    }
    // If body already contains a composition uid it must match the {versioned_object_uid} in request url
    Optional<String> inputUuid = Optional.ofNullable(compositionService.getUidFromInputComposition(composition, compositionFormat));
    inputUuid.ifPresent(id -> {
        // TODO it is further unclear what exactly the REST spec's "match" means, see: https://github.com/openEHR/specifications-ITS-REST/issues/83
        if (!versionedObjectUid.equals(extractVersionedObjectUidFromVersionUid(id))) {
            throw new PreconditionFailedException("UUID from input must match given versioned_object_uid in request URL");
        }
    });
    // variable to overload with more specific object if requested
    Optional<InternalResponse<CompositionResponseData>> respData = Optional.empty();
    try {
        Composition compoObj = compositionService.buildComposition(composition, compositionFormat, null);
        // TODO should have EHR as parameter and check for existence as precondition - see EHR-245 (no direct EHR access in this controller)
        // ifMatch header has to be tested for correctness already above
        Optional<CompositionDto> dtoOptional = compositionService.update(ehrId, new ObjectVersionId(ifMatch), compoObj);
        var compositionVersionUid = dtoOptional.orElseThrow(() -> new InternalServerException("Failed to create composition")).getComposition().getUid().toString();
        var uri = URI.create(this.encodePath(getBaseEnvLinkURL() + "/rest/openehr/v1/ehr/" + ehrId.toString() + "/composition/" + compositionVersionUid));
        List<String> headerList = Arrays.asList(LOCATION, ETAG, // whatever is required by REST spec - CONTENT_TYPE only needed for 200, so handled separately
        LAST_MODIFIED);
        UUID compositionId = extractVersionedObjectUidFromVersionUid(compositionVersionUid);
        if (RETURN_REPRESENTATION.equals(prefer)) {
            // both options extract needed info from versionUid
            respData = buildCompositionResponseData(compositionId, extractVersionFromVersionUid(compositionVersionUid), accept, uri, headerList, () -> new CompositionResponseData(null, null));
        } else {
            // "minimal" is default fallback
            respData = buildCompositionResponseData(compositionId, extractVersionFromVersionUid(compositionVersionUid), accept, uri, headerList, () -> null);
        }
        // Enriches request attributes with current compositionId for later audit processing
        request.setAttribute(OpenEhrAuditInterceptor.EHR_ID_ATTRIBUTE, Collections.singleton(ehrId));
        request.setAttribute(CompositionAuditInterceptor.COMPOSITION_ID_ATTRIBUTE, compositionId);
    } catch (ObjectNotFoundException e) {
        // composition not found
        return ResponseEntity.notFound().build();
    }
    // returns 200 with body + headers, 204 only with headers or 500 error depending on what processing above yields
    return respData.map(i -> Optional.ofNullable(i.getResponseData()).map(StructuredString::getValue).map(j -> ResponseEntity.ok().headers(i.getHeaders()).body(j)).orElse(ResponseEntity.noContent().headers(i.getHeaders()).build())).orElse(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
}
Also used : CompositionResponseData(org.ehrbase.response.openehr.CompositionResponseData) PathVariable(org.springframework.web.bind.annotation.PathVariable) Arrays(java.util.Arrays) RequestParam(org.springframework.web.bind.annotation.RequestParam) Composition(com.nedap.archie.rm.composition.Composition) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize) ZonedDateTime(java.time.ZonedDateTime) LocalDateTime(java.time.LocalDateTime) ObjectNotFoundException(org.ehrbase.api.exception.ObjectNotFoundException) Autowired(org.springframework.beans.factory.annotation.Autowired) RequestMapping(org.springframework.web.bind.annotation.RequestMapping) Supplier(java.util.function.Supplier) RequestBody(org.springframework.web.bind.annotation.RequestBody) HttpServletRequest(javax.servlet.http.HttpServletRequest) PutMapping(org.springframework.web.bind.annotation.PutMapping) CompositionResponseData(org.ehrbase.response.openehr.CompositionResponseData) CompositionAuditInterceptor(org.ehrbase.rest.openehr.audit.CompositionAuditInterceptor) GetMapping(org.springframework.web.bind.annotation.GetMapping) URI(java.net.URI) PreconditionFailedException(org.ehrbase.api.exception.PreconditionFailedException) ObjectVersionId(com.nedap.archie.rm.support.identification.ObjectVersionId) ResponseStatus(org.springframework.web.bind.annotation.ResponseStatus) DeleteMapping(org.springframework.web.bind.annotation.DeleteMapping) PostMapping(org.springframework.web.bind.annotation.PostMapping) HttpHeaders(org.springframework.http.HttpHeaders) MediaType(org.springframework.http.MediaType) CompositionDto(org.ehrbase.response.ehrscape.CompositionDto) PostAuthorize(org.springframework.security.access.prepost.PostAuthorize) UUID(java.util.UUID) RestController(org.springframework.web.bind.annotation.RestController) ZoneId(java.time.ZoneId) StructuredString(org.ehrbase.response.ehrscape.StructuredString) BaseController(org.ehrbase.rest.BaseController) Objects(java.util.Objects) HttpStatus(org.springframework.http.HttpStatus) List(java.util.List) OffsetDateTime(java.time.OffsetDateTime) CompositionService(org.ehrbase.api.service.CompositionService) InternalServerException(org.ehrbase.api.exception.InternalServerException) OpenEhrAuditInterceptor(org.ehrbase.rest.openehr.audit.OpenEhrAuditInterceptor) InternalResponse(org.ehrbase.rest.util.InternalResponse) Optional(java.util.Optional) CompositionApiSpecification(org.ehrbase.rest.openehr.specification.CompositionApiSpecification) ResponseEntity(org.springframework.http.ResponseEntity) RequestHeader(org.springframework.web.bind.annotation.RequestHeader) Collections(java.util.Collections) CompositionFormat(org.ehrbase.response.ehrscape.CompositionFormat) CompositionFormat(org.ehrbase.response.ehrscape.CompositionFormat) Composition(com.nedap.archie.rm.composition.Composition) InternalServerException(org.ehrbase.api.exception.InternalServerException) StructuredString(org.ehrbase.response.ehrscape.StructuredString) InternalResponse(org.ehrbase.rest.util.InternalResponse) ObjectVersionId(com.nedap.archie.rm.support.identification.ObjectVersionId) ObjectNotFoundException(org.ehrbase.api.exception.ObjectNotFoundException) CompositionDto(org.ehrbase.response.ehrscape.CompositionDto) StructuredString(org.ehrbase.response.ehrscape.StructuredString) PreconditionFailedException(org.ehrbase.api.exception.PreconditionFailedException) UUID(java.util.UUID) PutMapping(org.springframework.web.bind.annotation.PutMapping) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Example 2 with CompositionResponseData

use of org.ehrbase.response.openehr.CompositionResponseData in project ehrbase by ehrbase.

the class OpenehrCompositionController method getCompositionByTime.

/**
 * This mapping combines both GETs "/{ehr_id}/composition/{version_uid}" (via overlapping path)
 * and "/{ehr_id}/composition/{versioned_object_uid}{?version_at_time}" (here). This is necessary
 * because of the overlapping paths. Both mappings are specified to behave almost the same, so
 * this solution works in this case.
 */
@GetMapping("/{ehr_id}/composition/{versioned_object_uid}{?version_at_time}")
// checkAbacPre /-Post attributes (type, subject, payload, content type)
@PostAuthorize("checkAbacPost(@openehrCompositionController.COMPOSITION, " + "@ehrService.getSubjectExtRef(#ehrIdString), returnObject, #accept)")
@Override
public ResponseEntity getCompositionByTime(@RequestHeader(value = ACCEPT, required = false) String accept, @PathVariable(value = "ehr_id") String ehrIdString, @PathVariable(value = "versioned_object_uid") String versionedObjectUid, @RequestParam(value = "version_at_time", required = false) String versionAtTime, HttpServletRequest request) {
    UUID ehrId = getEhrUuid(ehrIdString);
    // Note: Since this method can be called by another mapping as "almost overloaded" function some parameters might be semantically named wrong in that case. E.g. versionedObjectUid can contain a versionUid.
    // Note: versionUid should be of format "uuid::domain::version", versionObjectUid of format "uuid"
    UUID compositionUid = extractVersionedObjectUidFromVersionUid(// extracts UUID from long or short notation
    versionedObjectUid);
    if (compositionService.isDeleted(compositionUid)) {
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
    // fallback 0 means latest version
    int version = 0;
    if (extractVersionFromVersionUid(versionedObjectUid) != 0) {
        // the given ID contains a version, therefore this is case GET {version_uid}
        version = extractVersionFromVersionUid(versionedObjectUid);
    } else {
        // case GET {versioned_object_uid}{?version_at_time}
        Optional<OffsetDateTime> temporal = getVersionAtTimeParam();
        if (versionAtTime != null && temporal.isPresent()) {
            // when optional request parameter was provided, retrieve version according to given time
            Optional<Integer> versionFromTimestamp = Optional.ofNullable(compositionService.getVersionByTimestamp(compositionUid, temporal.get().toLocalDateTime()));
            version = versionFromTimestamp.orElseThrow(() -> new ObjectNotFoundException("composition", "No composition version matching the timestamp condition"));
        }
    // else continue with fallback: latest version
    }
    URI uri = URI.create(this.encodePath(getBaseEnvLinkURL() + "/rest/openehr/v1/ehr/" + ehrId.toString() + "/composition/" + versionedObjectUid));
    List<String> headerList = Arrays.asList(LOCATION, ETAG, // whatever is required by REST spec - CONTENT_TYPE only needed for 200, so handled separately
    LAST_MODIFIED);
    Optional<InternalResponse<CompositionResponseData>> respData = buildCompositionResponseData(compositionUid, version, accept, uri, headerList, () -> new CompositionResponseData(null, null));
    // Enriches request attributes with ehrId, compositionId and version for later audit processing
    request.setAttribute(OpenEhrAuditInterceptor.EHR_ID_ATTRIBUTE, Collections.singleton(ehrId));
    request.setAttribute(CompositionAuditInterceptor.COMPOSITION_ID_ATTRIBUTE, compositionUid);
    request.setAttribute(CompositionAuditInterceptor.VERSION_ATTRIBUTE, version);
    // returns 200 with body + headers, 204 only with headers or 500 error depending on what processing above yields
    return respData.map(i -> Optional.ofNullable(i.getResponseData().getValue()).map(j -> ResponseEntity.ok().headers(i.getHeaders()).body(j)).orElse(ResponseEntity.noContent().headers(i.getHeaders()).build())).orElse(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build());
}
Also used : CompositionResponseData(org.ehrbase.response.openehr.CompositionResponseData) PathVariable(org.springframework.web.bind.annotation.PathVariable) Arrays(java.util.Arrays) RequestParam(org.springframework.web.bind.annotation.RequestParam) Composition(com.nedap.archie.rm.composition.Composition) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize) ZonedDateTime(java.time.ZonedDateTime) LocalDateTime(java.time.LocalDateTime) ObjectNotFoundException(org.ehrbase.api.exception.ObjectNotFoundException) Autowired(org.springframework.beans.factory.annotation.Autowired) RequestMapping(org.springframework.web.bind.annotation.RequestMapping) Supplier(java.util.function.Supplier) RequestBody(org.springframework.web.bind.annotation.RequestBody) HttpServletRequest(javax.servlet.http.HttpServletRequest) PutMapping(org.springframework.web.bind.annotation.PutMapping) CompositionResponseData(org.ehrbase.response.openehr.CompositionResponseData) CompositionAuditInterceptor(org.ehrbase.rest.openehr.audit.CompositionAuditInterceptor) GetMapping(org.springframework.web.bind.annotation.GetMapping) URI(java.net.URI) PreconditionFailedException(org.ehrbase.api.exception.PreconditionFailedException) ObjectVersionId(com.nedap.archie.rm.support.identification.ObjectVersionId) ResponseStatus(org.springframework.web.bind.annotation.ResponseStatus) DeleteMapping(org.springframework.web.bind.annotation.DeleteMapping) PostMapping(org.springframework.web.bind.annotation.PostMapping) HttpHeaders(org.springframework.http.HttpHeaders) MediaType(org.springframework.http.MediaType) CompositionDto(org.ehrbase.response.ehrscape.CompositionDto) PostAuthorize(org.springframework.security.access.prepost.PostAuthorize) UUID(java.util.UUID) RestController(org.springframework.web.bind.annotation.RestController) ZoneId(java.time.ZoneId) StructuredString(org.ehrbase.response.ehrscape.StructuredString) BaseController(org.ehrbase.rest.BaseController) Objects(java.util.Objects) HttpStatus(org.springframework.http.HttpStatus) List(java.util.List) OffsetDateTime(java.time.OffsetDateTime) CompositionService(org.ehrbase.api.service.CompositionService) InternalServerException(org.ehrbase.api.exception.InternalServerException) OpenEhrAuditInterceptor(org.ehrbase.rest.openehr.audit.OpenEhrAuditInterceptor) InternalResponse(org.ehrbase.rest.util.InternalResponse) Optional(java.util.Optional) CompositionApiSpecification(org.ehrbase.rest.openehr.specification.CompositionApiSpecification) ResponseEntity(org.springframework.http.ResponseEntity) RequestHeader(org.springframework.web.bind.annotation.RequestHeader) Collections(java.util.Collections) CompositionFormat(org.ehrbase.response.ehrscape.CompositionFormat) StructuredString(org.ehrbase.response.ehrscape.StructuredString) InternalResponse(org.ehrbase.rest.util.InternalResponse) URI(java.net.URI) ResponseEntity(org.springframework.http.ResponseEntity) OffsetDateTime(java.time.OffsetDateTime) ObjectNotFoundException(org.ehrbase.api.exception.ObjectNotFoundException) UUID(java.util.UUID) GetMapping(org.springframework.web.bind.annotation.GetMapping) PostAuthorize(org.springframework.security.access.prepost.PostAuthorize)

Example 3 with CompositionResponseData

use of org.ehrbase.response.openehr.CompositionResponseData in project ehrbase by ehrbase.

the class OpenehrCompositionController method buildCompositionResponseData.

/**
 * Builder method to prepare appropriate HTTP response. Flexible to either allow minimal or full
 * representation of resource.
 *
 * @param <T>           Type of the response body
 * @param compositionId ID of the composition
 * @param version       0 if latest, otherwise integer of specific version.
 * @param accept        Format the response should be delivered in, as given by request
 * @param uri           Location of resource
 * @param headerList    List of headers to be set for response
 * @param factory       Lambda function to constructor of desired object
 * @return
 */
private <T extends CompositionResponseData> Optional<InternalResponse<T>> buildCompositionResponseData(UUID compositionId, Integer version, String accept, URI uri, List<String> headerList, Supplier<T> factory) {
    // create either CompositionResponseData or null (means no body, only headers incl. link to resource), via lambda request
    T minimalOrRepresentation = factory.get();
    // do minimal scope steps
    // create and supplement headers with data depending on which headers are requested
    HttpHeaders respHeaders = new HttpHeaders();
    for (String header : headerList) {
        switch(header) {
            case LOCATION:
                respHeaders.setLocation(uri);
                break;
            case ETAG:
                respHeaders.setETag("\"" + compositionId + "::" + compositionService.getServerConfig().getNodename() + "::" + compositionService.getLastVersionNumber(compositionId) + "\"");
                break;
            case LAST_MODIFIED:
                // TODO should be VERSION.commit_audit.time_committed.value which is not implemented yet - mock for now
                respHeaders.setLastModified(123124442);
                break;
            default:
        }
    }
    // if (minimalOrRepresentation.getClass().equals(CompositionResponseData.class)) {     // TODO make Optional.ofNull....
    if (minimalOrRepresentation != null) {
        // when this "if" is true the following casting can be executed and data manipulated by reference (handled by temporary variable)
        CompositionResponseData objByReference = (CompositionResponseData) minimalOrRepresentation;
        CompositionFormat format = extractCompositionFormat(accept);
        // version handling allows to request specific version
        Integer versionNumber = version;
        if (versionNumber == 0) {
            versionNumber = compositionService.getLastVersionNumber(compositionId);
        }
        Optional<CompositionDto> compositionDto = compositionService.retrieve(compositionId, versionNumber);
        // TODO how to handle error situation here only with Optional? is there a better way without java 9 Optional.ifPresentOrElse()?
        if (compositionDto.isPresent()) {
            StructuredString ss = compositionService.serialize(compositionDto.get(), format);
            objByReference.setValue(ss.getValue());
            objByReference.setFormat(ss.getFormat());
        // objByReference.setComposition(compositionService.serialize(compositionDto.get(), format));
        } else {
            // TODO undo creation of composition, if applicable
            throw new ObjectNotFoundException("composition", "Couldn't retrieve composition");
        }
        // finally set last header
        if (format.equals(CompositionFormat.XML)) {
            respHeaders.setContentType(MediaType.APPLICATION_XML);
        } else if (format.equals(CompositionFormat.FLAT) || format.equals(CompositionFormat.ECISFLAT) || format.equals(CompositionFormat.RAW)) {
            respHeaders.setContentType(MediaType.APPLICATION_JSON);
        }
    }
    return Optional.of(new InternalResponse<>(minimalOrRepresentation, respHeaders));
}
Also used : CompositionResponseData(org.ehrbase.response.openehr.CompositionResponseData) HttpHeaders(org.springframework.http.HttpHeaders) CompositionFormat(org.ehrbase.response.ehrscape.CompositionFormat) ObjectNotFoundException(org.ehrbase.api.exception.ObjectNotFoundException) CompositionDto(org.ehrbase.response.ehrscape.CompositionDto) StructuredString(org.ehrbase.response.ehrscape.StructuredString) StructuredString(org.ehrbase.response.ehrscape.StructuredString)

Aggregations

ObjectNotFoundException (org.ehrbase.api.exception.ObjectNotFoundException)3 CompositionDto (org.ehrbase.response.ehrscape.CompositionDto)3 CompositionFormat (org.ehrbase.response.ehrscape.CompositionFormat)3 StructuredString (org.ehrbase.response.ehrscape.StructuredString)3 CompositionResponseData (org.ehrbase.response.openehr.CompositionResponseData)3 HttpHeaders (org.springframework.http.HttpHeaders)3 Composition (com.nedap.archie.rm.composition.Composition)2 ObjectVersionId (com.nedap.archie.rm.support.identification.ObjectVersionId)2 URI (java.net.URI)2 LocalDateTime (java.time.LocalDateTime)2 OffsetDateTime (java.time.OffsetDateTime)2 ZoneId (java.time.ZoneId)2 ZonedDateTime (java.time.ZonedDateTime)2 Arrays (java.util.Arrays)2 Collections (java.util.Collections)2 List (java.util.List)2 Objects (java.util.Objects)2 Optional (java.util.Optional)2 UUID (java.util.UUID)2 Supplier (java.util.function.Supplier)2