From 916e61819a707aa447c7c4c44ac50717cf35b7b9 Mon Sep 17 00:00:00 2001 From: Harsh Patel Date: Wed, 30 Dec 2020 10:59:23 -0500 Subject: [PATCH] OpenConceptLab/ocl_issues#271 developed code system create operation and code refactoring --- .../fhir/OclFhirApplication.java | 11 +- .../controller/BaseOclFhirController.java | 191 ++++++++ .../fhir/controller/OclFhirController.java | 410 +----------------- .../fhir/controller/OclFhirOrgController.java | 185 ++++++++ .../controller/OclFhirUserController.java | 185 ++++++++ .../fhir/converter/BaseConverter.java | 321 ++++++++++++++ .../fhir/converter/CodeSystemConverter.java | 410 ++++++++++++------ .../fhir/converter/ValueSetConverter.java | 47 +- .../OclFhirAuthorizationInterceptor.java | 3 +- .../org/openconceptlab/fhir/model/Source.java | 10 +- .../fhir/model/UserProfilesOrganization.java | 1 + .../provider/CodeSystemResourceProvider.java | 18 + .../provider/ValueSetResourceProvider.java | 19 +- .../fhir/repository/AuthtokenRepository.java | 9 + .../fhir/repository/ConceptRepository.java | 5 + .../repository/OrganizationRepository.java | 3 + .../fhir/repository/SourceRepository.java | 7 + .../UserProfilesOrganizationRepository.java | 13 + .../fhir/repository/UserRepository.java | 4 +- .../fhir/util/OclFhirConstants.java | 42 +- .../openconceptlab/fhir/util/OclFhirUtil.java | 112 +++-- .../openconceptlab/fhir/base/OclFhirTest.java | 153 +++++-- .../TestCodeSystemResourceProvider.java | 225 +++++++++- .../TestOclCapabilityStatementProvider.java | 2 + .../TestValueSetResourceProvider.java | 17 +- 25 files changed, 1685 insertions(+), 718 deletions(-) create mode 100644 ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/BaseOclFhirController.java create mode 100644 ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirOrgController.java create mode 100644 ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirUserController.java create mode 100644 ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/BaseConverter.java create mode 100644 ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/AuthtokenRepository.java create mode 100644 ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserProfilesOrganizationRepository.java diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/OclFhirApplication.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/OclFhirApplication.java index 8ec32ce..70e3cdb 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/OclFhirApplication.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/OclFhirApplication.java @@ -3,6 +3,7 @@ import org.openconceptlab.fhir.config.Config; import org.openconceptlab.fhir.controller.OclFhirController; import org.openconceptlab.fhir.converter.CodeSystemConverter; +import org.openconceptlab.fhir.util.OclFhirUtil; import org.openconceptlab.fhir.interceptor.OclFhirLoggingInterceptor; import org.openconceptlab.fhir.model.BaseOclEntity; import org.openconceptlab.fhir.model.UserProfile; @@ -10,7 +11,6 @@ import org.openconceptlab.fhir.repository.BaseOclRepository; import org.openconceptlab.fhir.repository.ConceptRepository; import org.openconceptlab.fhir.repository.UserRepository; -import org.openconceptlab.fhir.util.OclFhirUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -21,12 +21,9 @@ import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.PropertySource; -import org.springframework.context.annotation.PropertySources; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import javax.annotation.PostConstruct; -import java.util.List; @ServletComponentScan @SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class}) @@ -57,9 +54,9 @@ protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { @PostConstruct public void init() { - List users = userRepository.findByUsernameIs("ocladmin"); - if (!users.isEmpty()) { - oclUser = users.get(0); + UserProfile user = userRepository.findByUsername("ocladmin"); + if (user != null) { + oclUser = user; } else { throw new InternalError("Can not find ocladmin user."); } diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/BaseOclFhirController.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/BaseOclFhirController.java new file mode 100644 index 0000000..84b2d04 --- /dev/null +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/BaseOclFhirController.java @@ -0,0 +1,191 @@ +package org.openconceptlab.fhir.controller; + +import ca.uhn.fhir.rest.gclient.IQuery; +import ca.uhn.fhir.rest.gclient.StringClientParam; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import org.hl7.fhir.r4.model.*; +import org.openconceptlab.fhir.util.OclFhirUtil; +import org.openconceptlab.fhir.provider.CodeSystemResourceProvider; +import org.openconceptlab.fhir.provider.ValueSetResourceProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; + +import static org.openconceptlab.fhir.util.OclFhirUtil.*; +import static org.openconceptlab.fhir.util.OclFhirUtil.getIdentifier; +import static org.openconceptlab.fhir.util.OclFhirConstants.*; +import static org.openconceptlab.fhir.util.OclFhirConstants.FW_SLASH; + +@Component +public class BaseOclFhirController { + + CodeSystemResourceProvider codeSystemResourceProvider; + ValueSetResourceProvider valueSetResourceProvider; + OclFhirUtil oclFhirUtil; + + @Autowired + public BaseOclFhirController(CodeSystemResourceProvider codeSystemResourceProvider, + ValueSetResourceProvider valueSetResourceProvider, + OclFhirUtil oclFhirUtil) { + this.codeSystemResourceProvider = codeSystemResourceProvider; + this.valueSetResourceProvider = valueSetResourceProvider; + this.oclFhirUtil = oclFhirUtil; + } + + protected ResponseEntity handleSearchResource(final Class resourceClass, final String... args) { + try { + String resource = searchResource(resourceClass, args); + return ResponseEntity.ok(resource); + } catch (BaseServerResponseException e) { + return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBody()); + } catch (Exception e) { + return badRequest(); + } + } + + protected ResponseEntity handleFhirOperation(Parameters parameters, Class type, String operation) { + try { + return ResponseEntity.ok(oclFhirUtil.getResourceAsString(performFhirOperation(parameters, type, operation))); + } catch (BaseServerResponseException e) { + return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBody()); + } catch (Exception e) { + return badRequest(); + } + } + + protected String searchResource(final Class resourceClass, final String... filters) { + IQuery q = oclFhirUtil.getClient().search().forResource(resourceClass); + if (filters.length % 2 == 0) { + for (int i = 0; i < filters.length; i += 2) { + if (i == 0) { + q = q.where(new StringClientParam(filters[i]).matches().value(filters[i + 1])); + } else { + q = q.and(new StringClientParam(filters[i]).matches().value(filters[i + 1])); + } + } + } + Bundle bundle = (Bundle) q.execute(); + return oclFhirUtil.getResourceAsString(bundle); + } + + protected Parameters performFhirOperation(Parameters parameters, Class type, String operation) { + return oclFhirUtil.getClient() + .operation() + .onType(type) + .named(operation) + .withParameters(parameters) + .execute(); + } + + protected void performCreate(CodeSystem resource, String auth) { + oclFhirUtil.getClient() + .create() + .resource(resource).withAdditionalHeader(AUTHORIZATION, auth) + .execute(); + } + + protected Parameters generateParameters(String code, String displayLanguage, String owner) { + Parameters parameters = new Parameters(); + parameters.addParameter().setName(CODE).setValue(new CodeType(code)); + if (isValid(displayLanguage)) + parameters.addParameter().setName(DISP_LANG).setValue(new CodeType(displayLanguage)); + parameters.addParameter().setName(OWNER).setValue(newStringType(owner)); + return parameters; + } + + protected Parameters lookupParameters(String system, String code, String version, String displayLanguage, String owner) { + Parameters parameters = generateParameters(code, displayLanguage, owner); + parameters.addParameter().setName(SYSTEM).setValue(new UriType(system)); + if (isValid(version)) + parameters.addParameter().setName(VERSION).setValue(newStringType(version)); + return parameters; + } + + protected Parameters codeSystemVCParameters(String url, String code, String version, String display, String displayLanguage, + String owner) { + Parameters parameters = generateParameters(code, displayLanguage, owner); + parameters.addParameter().setName(URL).setValue(new UriType(url)); + if (isValid(version)) + parameters.addParameter().setName(VERSION).setValue(newStringType(version)); + if (isValid(display)) + parameters.addParameter().setName(DISPLAY).setValue(newStringType(display)); + return parameters; + } + + protected Parameters valueSetVCParameters(String url, String valueSetId, String valueSetVersion, String code, String system, String systemVersion, + String display, String displayLanguage, String owner) { + Parameters parameters = generateParameters(code, displayLanguage, owner); + parameters.addParameter().setName(SYSTEM).setValue(new UriType(system)); + if (isValid(url)) + parameters.addParameter().setName(URL).setValue(new UriType(url)); + if (isValid(valueSetId)) + parameters.addParameter().setName("valueSetId").setValue(newStringType(valueSetId)); + if (isValid(systemVersion)) + parameters.addParameter().setName(SYSTEM_VERSION).setValue(newStringType(systemVersion)); + if (isValid(valueSetVersion)) + parameters.addParameter().setName(VALUESET_VERSION).setValue(newStringType(valueSetVersion)); + if (isValid(display)) + parameters.addParameter().setName(DISPLAY).setValue(newStringType(display)); + return parameters; + } + + protected Parameters valueSetExpandParameters(String url, String valueSetVersion, Integer offset, Integer count, Boolean includeDesignations, + Boolean includeDefinition, Boolean activeOnly, String displayLanguage, String filter, String owner) { + Parameters parameters = new Parameters(); + if (isValid(url)) + parameters.addParameter().setName(URL).setValue(newUri(url)); + if (isValid(valueSetVersion)) + parameters.addParameter().setName(VALUESET_VERSION).setValue(newStringType(valueSetVersion)); + if (isValid(displayLanguage)) + parameters.addParameter().setName(DISPLAY_LANGUAGE).setValue(newStringType(displayLanguage)); + if (isValid(filter)) + parameters.addParameter().setName(FILTER).setValue(newStringType(filter)); + parameters.addParameter().setName(OFFSET).setValue(newInteger(offset)); + parameters.addParameter().setName(COUNT).setValue(newInteger(count)); + parameters.addParameter().setName(INCLUDE_DESIGNATIONS).setValue(newBoolean(includeDesignations)); + parameters.addParameter().setName(INCLUDE_DEFINITION).setValue(newBoolean(includeDefinition)); + parameters.addParameter().setName(ACTIVE_ONLY).setValue(newBoolean(activeOnly)); + parameters.addParameter().setName(OWNER).setValue(newStringType(owner)); + return parameters; + } + + protected static String formatOrg(String org) { + return ORG_ + org; + } + + protected static String formatUser(String user) { + return USER_ + user; + } + + protected ResponseEntity validate(String user, CodeSystem system, Optional acsnOpt, + String ownerType, String ownerId) { + ResponseEntity response1 = validateId(system.getId()); + if (response1 != null) return response1; + return validateAccessionId(acsnOpt, ownerType, ownerId); + } + + protected ResponseEntity validateId(String id) { + if (!isValid(id)) + return badRequest("The id can not be empty."); + return null; + } + + protected ResponseEntity validateAccessionId(Optional acsnOpt, String ownerType, String ownerId) { + if (acsnOpt.isPresent()) { + String[] values = formatExpression(acsnOpt.get().getValue()).split(FW_SLASH); + if (!(values.length >= 3 && ownerType.equals(values[1]) && ownerId.equals(values[2]))) { + return badRequest("The Accession id does not match with given request."); + } + } + return null; + } + + protected void addIdentifier(List identifiers, String ownerType, String ownerId, String resType, String resId) { + identifiers.add(getIdentifier(FW_SLASH + ownerType + FW_SLASH + ownerId + FW_SLASH + resType + FW_SLASH + resId + FW_SLASH).get()); + } + +} + diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirController.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirController.java index ff5209c..422e937 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirController.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirController.java @@ -1,426 +1,24 @@ package org.openconceptlab.fhir.controller; -import ca.uhn.fhir.rest.gclient.IQuery; -import ca.uhn.fhir.rest.gclient.StringClientParam; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import org.hl7.fhir.r4.model.*; import org.openconceptlab.fhir.provider.CodeSystemResourceProvider; import org.openconceptlab.fhir.provider.ValueSetResourceProvider; import org.openconceptlab.fhir.util.OclFhirUtil; - -import static org.openconceptlab.fhir.util.OclFhirConstants.*; -import static org.openconceptlab.fhir.util.OclFhirUtil.*; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.Optional; - /** - * The OclFhirController class. This is used to support OCL compatible end points. + * The OclFhirController class. This is used to support base ocl end points. * * @author harpatel1 */ @RestController @RequestMapping({"/"}) -public class OclFhirController { +public class OclFhirController extends BaseOclFhirController{ - CodeSystemResourceProvider codeSystemResourceProvider; - ValueSetResourceProvider valueSetResourceProvider; - OclFhirUtil oclFhirUtil; - - @Autowired public OclFhirController(CodeSystemResourceProvider codeSystemResourceProvider, ValueSetResourceProvider valueSetResourceProvider, OclFhirUtil oclFhirUtil) { - this.codeSystemResourceProvider = codeSystemResourceProvider; - this.valueSetResourceProvider = valueSetResourceProvider; - this.oclFhirUtil = oclFhirUtil; - } - - @GetMapping(path = {"/orgs/{org}/CodeSystem/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getCodeSystemByOrg(@PathVariable(name = ORG) String org, - @PathVariable(name = ID) String id, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id, PAGE, page); - return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id); - } - - @GetMapping(path = {"/orgs/{org}/CodeSystem/{id}/version", - "/orgs/{org}/CodeSystem/{id}/version/{version}"}, - produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getCodeSystemVersionsByOrg(@PathVariable(name = ORG) String org, - @PathVariable(name = ID) String id, - @PathVariable(name = VERSION) Optional version, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL), PAGE, page); - return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL)); - } - - @GetMapping(path = {"/orgs/{org}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity searchCodeSystemsByOrg(@PathVariable String org) { - return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org)); - } - - @GetMapping(path = {"/orgs/{org}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity lookUpCodeSystemsByOrg(@PathVariable String org, - @RequestParam(name = SYSTEM) String system, - @RequestParam(name = CODE) String code, - @RequestParam(name = VERSION, required = false) String version, - @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { - Parameters parameters = lookupParameters(system, code, version, displayLanguage, formatOrg(org)); - return handleFhirOperation(parameters, CodeSystem.class, LOOKUP); - } - - @PostMapping(path = {"/orgs/{org}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity lookUpCodeSystemsByOrg(@PathVariable String org, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); - return handleFhirOperation(params, CodeSystem.class, LOOKUP); - } - - @GetMapping(path = {"/orgs/{org}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateCodeSystemsByOrg(@PathVariable String org, - @RequestParam(name = URL) String url, - @RequestParam(name = CODE) String code, - @RequestParam(name = VERSION, required = false) String version, - @RequestParam(name = DISPLAY, required = false) String display, - @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { - Parameters parameters = codeSystemVCParameters(url, code, version, display, displayLanguage, formatOrg(org)); - return handleFhirOperation(parameters, CodeSystem.class, VALIDATE_CODE); - } - - @PostMapping(path = {"/orgs/{org}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateCodeSystemsByOrg(@PathVariable String org, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); - return handleFhirOperation(params, CodeSystem.class, VALIDATE_CODE); - } - - @GetMapping(path = {"/orgs/{org}/ValueSet/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getValueSetByOrg(@PathVariable String org, - @PathVariable String id, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id, PAGE, page); - return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id); - } - - @GetMapping(path = {"/orgs/{org}/ValueSet/{id}/version", - "/orgs/{org}/ValueSet/{id}/version/{version}"}, - produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getValueSetVersionsByOrg(@PathVariable(name = ORG) String org, - @PathVariable(name = ID) String id, - @PathVariable(name = VERSION) Optional version, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL), PAGE, page); - return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL)); - } - - @GetMapping(path = {"/orgs/{org}/ValueSet"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity searchValueSetsByOrg(@PathVariable String org) { - return handleSearchResource(ValueSet.class, OWNER, formatOrg(org)); - } - - @GetMapping(path = {"/orgs/{org}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateValueSetByOrg(@PathVariable String org, - @RequestParam(name = URL) String url, - @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, - @RequestParam(name = CODE) String code, - @RequestParam(name = SYSTEM) String system, - @RequestParam(name = SYSTEM_VERSION, required = false) String systemVersion, - @RequestParam(name = DISPLAY, required = false) String display, - @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { - - Parameters parameters = valueSetVCParameters(url, EMPTY, valueSetVersion, code, system, systemVersion, display, - displayLanguage, formatOrg(org)); - return handleFhirOperation(parameters, ValueSet.class, VALIDATE_CODE); - } - - @PostMapping(path = {"/orgs/{org}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateValueSetByOrg(@PathVariable String org, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); - return handleFhirOperation(params, ValueSet.class, VALIDATE_CODE); - } - - @GetMapping(path = {"/orgs/{org}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity expandValueSetByOrg(@PathVariable String org, - @RequestParam(name = URL) String url, - @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, - @RequestParam(name = OFFSET, required = false, defaultValue = "0") Integer offset, - @RequestParam(name = COUNT, required = false, defaultValue = "100") Integer count, - @RequestParam(name = INCLUDE_DESIGNATIONS, defaultValue = "true") Boolean includeDesignations, - @RequestParam(name = INCLUDE_DEFINITION, defaultValue = "false") Boolean includeDefinition, - @RequestParam(name = ACTIVE_ONLY, defaultValue = "true") Boolean activeOnly, - @RequestParam(name = DISPLAY_LANGUAGE, required = false) String displayLanguage, - @RequestParam(name = FILTER, required = false) String filter) { - - Parameters parameters = valueSetExpandParameters(url, valueSetVersion, offset, count, includeDesignations, - includeDefinition, activeOnly, displayLanguage, filter, formatOrg(org)); - return handleFhirOperation(parameters, ValueSet.class, EXPAND); - } - - @PostMapping(path = {"/orgs/{org}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity expandValueSetByOrg(@PathVariable String org, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); - return handleFhirOperation(params, ValueSet.class, EXPAND); - } - - @GetMapping(path = {"/users/{user}/CodeSystem/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getCodeSystemByUser(@PathVariable String user, - @PathVariable String id, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id, PAGE, page); - return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id); - } - - @GetMapping(path = {"/users/{user}/CodeSystem/{id}/version", - "/users/{user}/CodeSystem/{id}/version/{version}"}, - produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getCodeSystemVersionsByUser(@PathVariable(name = USER) String user, - @PathVariable(name = ID) String id, - @PathVariable(name = VERSION) Optional version, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL), PAGE, page); - return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL)); - } - - @GetMapping(path = {"/users/{user}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity searchCodeSystemsByUser(@PathVariable String user) { - return handleSearchResource(CodeSystem.class, OWNER, formatUser(user)); - } - - @GetMapping(path = {"/users/{user}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity lookUpCodeSystemsByUser(@PathVariable String user, - @RequestParam(name = SYSTEM) String system, - @RequestParam(name = CODE) String code, - @RequestParam(name = VERSION, required = false) String version, - @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { - Parameters parameters = lookupParameters(system, code, version, displayLanguage, formatUser(user)); - return handleFhirOperation(parameters, CodeSystem.class, LOOKUP); - } - - @PostMapping(path = {"/users/{user}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity lookUpCodeSystemsByUser(@PathVariable String user, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); - return handleFhirOperation(params, CodeSystem.class, LOOKUP); - } - - @GetMapping(path = {"/users/{user}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateCodeSystemsByUser(@PathVariable String user, - @RequestParam(name = URL) String url, - @RequestParam(name = CODE) String code, - @RequestParam(name = VERSION, required = false) String version, - @RequestParam(name = DISPLAY, required = false) String display, - @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { - Parameters parameters = codeSystemVCParameters(url, code, version, display, displayLanguage, formatUser(user)); - return handleFhirOperation(parameters, CodeSystem.class, VALIDATE_CODE); - } - - @PostMapping(path = {"/users/{user}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateCodeSystemsByUser(@PathVariable String user, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); - return handleFhirOperation(params, CodeSystem.class, VALIDATE_CODE); - } - - @GetMapping(path = {"/users/{user}/ValueSet/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getValueSetByUser(@PathVariable String user, - @PathVariable String id, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id, PAGE, page); - return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id); - } - - @GetMapping(path = {"/users/{user}/ValueSet/{id}/version", - "/users/{user}/ValueSet/{id}/version/{version}"}, - produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity getValueSetVersionsByUser(@PathVariable(name = USER) String user, - @PathVariable(name = ID) String id, - @PathVariable(name = VERSION) Optional version, - @RequestParam(name = PAGE, required = false) String page) { - if (isValid(page)) - return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL), PAGE, page); - return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL)); - } - - @GetMapping(path = {"/users/{user}/ValueSet"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity searchValueSetsByUser(@PathVariable String user) { - return handleSearchResource(ValueSet.class, OWNER, formatUser(user)); + super(codeSystemResourceProvider, valueSetResourceProvider, oclFhirUtil); } - @GetMapping(path = {"/users/{user}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateValueSetByUser(@PathVariable String user, - @RequestParam(name = URL) String url, - @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, - @RequestParam(name = CODE) String code, - @RequestParam(name = SYSTEM) String system, - @RequestParam(name = SYSTEM_VERSION, required = false) String systemVersion, - @RequestParam(name = DISPLAY, required = false) String display, - @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { - - Parameters parameters = valueSetVCParameters(url, EMPTY, valueSetVersion, code, system, systemVersion, display, - displayLanguage, formatUser(user)); - return handleFhirOperation(parameters, ValueSet.class, VALIDATE_CODE); - } - - @PostMapping(path = {"/users/{user}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity validateValueSetByUser(@PathVariable String user, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); - return handleFhirOperation(params, ValueSet.class, VALIDATE_CODE); - } - - @GetMapping(path = {"/users/{user}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity expandValueSetByUser(@PathVariable String user, - @RequestParam(name = URL) String url, - @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, - @RequestParam(name = OFFSET, required = false, defaultValue = "0") Integer offset, - @RequestParam(name = COUNT, required = false, defaultValue = "100") Integer count, - @RequestParam(name = INCLUDE_DESIGNATIONS, defaultValue = "true") Boolean includeDesignations, - @RequestParam(name = INCLUDE_DEFINITION, defaultValue = "false") Boolean includeDefinition, - @RequestParam(name = ACTIVE_ONLY, defaultValue = "true") Boolean activeOnly, - @RequestParam(name = DISPLAY_LANGUAGE, required = false) String displayLanguage, - @RequestParam(name = FILTER, required = false) String filter) { - - Parameters parameters = valueSetExpandParameters(url, valueSetVersion, offset, count, includeDesignations, - includeDefinition, activeOnly, displayLanguage, filter, formatUser(user)); - return handleFhirOperation(parameters, ValueSet.class, EXPAND); - } - - @PostMapping(path = {"/users/{user}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity expandValueSetByUser(@PathVariable String user, @RequestBody String parameters){ - Parameters params = (Parameters) getResource(parameters); - params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); - return handleFhirOperation(params, ValueSet.class, EXPAND); - } - - private ResponseEntity handleSearchResource(final Class resourceClass, final String... args) { - try { - String resource = searchResource(resourceClass, args); - return ResponseEntity.ok(resource); - } catch (BaseServerResponseException e) { - return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBody()); - } catch (Exception e) { - return badRequest(); - } - } - - private ResponseEntity handleFhirOperation(Parameters parameters, Class type, String operation) { - try { - return ResponseEntity.ok(oclFhirUtil.getResourceAsString(performFhirOperation(parameters, type, operation))); - } catch (BaseServerResponseException e) { - return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBody()); - } catch (Exception e) { - return badRequest(); - } - } - - private String searchResource(final Class resourceClass, final String... filters) { - IQuery q = oclFhirUtil.getClient().search().forResource(resourceClass); - if (filters.length % 2 == 0) { - for (int i = 0; i < filters.length; i += 2) { - if (i == 0) { - q = q.where(new StringClientParam(filters[i]).matches().value(filters[i + 1])); - } else { - q = q.and(new StringClientParam(filters[i]).matches().value(filters[i + 1])); - } - } - } - Bundle bundle = (Bundle) q.execute(); - return oclFhirUtil.getResourceAsString(bundle); - } - - private Parameters performFhirOperation(Parameters parameters, Class type, String operation) { - return oclFhirUtil.getClient() - .operation() - .onType(type) - .named(operation) - .withParameters(parameters) - .execute(); - } - - private Parameters generateParameters(String code, String displayLanguage, String owner) { - Parameters parameters = new Parameters(); - parameters.addParameter().setName(CODE).setValue(new CodeType(code)); - if (isValid(displayLanguage)) - parameters.addParameter().setName(DISP_LANG).setValue(new CodeType(displayLanguage)); - parameters.addParameter().setName(OWNER).setValue(newStringType(owner)); - return parameters; - } - - private Parameters lookupParameters(String system, String code, String version, String displayLanguage, String owner) { - Parameters parameters = generateParameters(code, displayLanguage, owner); - parameters.addParameter().setName(SYSTEM).setValue(new UriType(system)); - if (isValid(version)) - parameters.addParameter().setName(VERSION).setValue(newStringType(version)); - return parameters; - } - - private Parameters codeSystemVCParameters(String url, String code, String version, String display, String displayLanguage, - String owner) { - Parameters parameters = generateParameters(code, displayLanguage, owner); - parameters.addParameter().setName(URL).setValue(new UriType(url)); - if (isValid(version)) - parameters.addParameter().setName(VERSION).setValue(newStringType(version)); - if (isValid(display)) - parameters.addParameter().setName(DISPLAY).setValue(newStringType(display)); - return parameters; - } - - private Parameters valueSetVCParameters(String url, String valueSetId, String valueSetVersion, String code, String system, String systemVersion, - String display, String displayLanguage, String owner) { - Parameters parameters = generateParameters(code, displayLanguage, owner); - parameters.addParameter().setName(SYSTEM).setValue(new UriType(system)); - if (isValid(url)) - parameters.addParameter().setName(URL).setValue(new UriType(url)); - if (isValid(valueSetId)) - parameters.addParameter().setName("valueSetId").setValue(newStringType(valueSetId)); - if (isValid(systemVersion)) - parameters.addParameter().setName(SYSTEM_VERSION).setValue(newStringType(systemVersion)); - if (isValid(valueSetVersion)) - parameters.addParameter().setName(VALUESET_VERSION).setValue(newStringType(valueSetVersion)); - if (isValid(display)) - parameters.addParameter().setName(DISPLAY).setValue(newStringType(display)); - return parameters; - } - - private Parameters valueSetExpandParameters(String url, String valueSetVersion, Integer offset, Integer count, Boolean includeDesignations, - Boolean includeDefinition, Boolean activeOnly, String displayLanguage, String filter, String owner) { - Parameters parameters = new Parameters(); - if (isValid(url)) - parameters.addParameter().setName(URL).setValue(newUri(url)); - if (isValid(valueSetVersion)) - parameters.addParameter().setName(VALUESET_VERSION).setValue(newStringType(valueSetVersion)); - if (isValid(displayLanguage)) - parameters.addParameter().setName(DISPLAY_LANGUAGE).setValue(newStringType(displayLanguage)); - if (isValid(filter)) - parameters.addParameter().setName(FILTER).setValue(newStringType(filter)); - parameters.addParameter().setName(OFFSET).setValue(newInteger(offset)); - parameters.addParameter().setName(COUNT).setValue(newInteger(count)); - parameters.addParameter().setName(INCLUDE_DESIGNATIONS).setValue(newBoolean(includeDesignations)); - parameters.addParameter().setName(INCLUDE_DEFINITION).setValue(newBoolean(includeDefinition)); - parameters.addParameter().setName(ACTIVE_ONLY).setValue(newBoolean(activeOnly)); - parameters.addParameter().setName(OWNER).setValue(newStringType(owner)); - return parameters; - } - - private static String formatOrg(String org) { - return ORG_ + org; - } - - private static String formatUser(String user) { - return USER_ + user; - } } + diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirOrgController.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirOrgController.java new file mode 100644 index 0000000..0ad39f4 --- /dev/null +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirOrgController.java @@ -0,0 +1,185 @@ +package org.openconceptlab.fhir.controller; + +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.ValueSet; +import org.openconceptlab.fhir.util.OclFhirUtil; +import org.openconceptlab.fhir.provider.CodeSystemResourceProvider; +import org.openconceptlab.fhir.provider.ValueSetResourceProvider; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +import static org.openconceptlab.fhir.util.OclFhirUtil.*; +import static org.openconceptlab.fhir.util.OclFhirConstants.*; +import static org.openconceptlab.fhir.util.OclFhirConstants.CODESYSTEM; + +@RestController +@RequestMapping({"/orgs"}) +public class OclFhirOrgController extends BaseOclFhirController { + + public OclFhirOrgController(CodeSystemResourceProvider codeSystemResourceProvider, + ValueSetResourceProvider valueSetResourceProvider, + OclFhirUtil oclFhirUtil) { + super(codeSystemResourceProvider, valueSetResourceProvider, oclFhirUtil); + } + + @PostMapping(path = {"/{org}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity createCodeSystemForOrg(@PathVariable(name = ORG) String org, + @RequestBody String codeSystem, + @RequestHeader(name = AUTHORIZATION) String auth) { + try { + CodeSystem system = (CodeSystem) parser.parseResource(codeSystem); + Optional acsnOpt = hasAccessionIdentifier(system.getIdentifier()); + ResponseEntity response = validate(org, system, acsnOpt, ORGS, org); + if (response != null) return response; + if (acsnOpt.isEmpty()) addIdentifier(system.getIdentifier(), ORGS, org, CODESYSTEM, system.getId()); + + performCreate(system, auth); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } catch (BaseServerResponseException e) { + return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBody()); + } catch (Exception e) { + return badRequest(); + } + } + + @GetMapping(path = {"/{org}/CodeSystem/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getCodeSystemByOrg(@PathVariable(name = ORG) String org, + @PathVariable(name = ID) String id, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id, PAGE, page); + return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id); + } + + @GetMapping(path = {"/{org}/CodeSystem/{id}/version", + "/{org}/CodeSystem/{id}/version/{version}"}, + produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getCodeSystemVersionsByOrg(@PathVariable(name = ORG) String org, + @PathVariable(name = ID) String id, + @PathVariable(name = VERSION) Optional version, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL), PAGE, page); + return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL)); + } + + @GetMapping(path = {"/{org}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity searchCodeSystemsByOrg(@PathVariable String org) { + return handleSearchResource(CodeSystem.class, OWNER, formatOrg(org)); + } + + @GetMapping(path = {"/{org}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity lookUpCodeSystemsByOrg(@PathVariable String org, + @RequestParam(name = SYSTEM) String system, + @RequestParam(name = CODE) String code, + @RequestParam(name = VERSION, required = false) String version, + @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { + Parameters parameters = lookupParameters(system, code, version, displayLanguage, formatOrg(org)); + return handleFhirOperation(parameters, CodeSystem.class, LOOKUP); + } + + @PostMapping(path = {"/{org}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity lookUpCodeSystemsByOrg(@PathVariable String org, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); + return handleFhirOperation(params, CodeSystem.class, LOOKUP); + } + + @GetMapping(path = {"/{org}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateCodeSystemsByOrg(@PathVariable String org, + @RequestParam(name = URL) String url, + @RequestParam(name = CODE) String code, + @RequestParam(name = VERSION, required = false) String version, + @RequestParam(name = DISPLAY, required = false) String display, + @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { + Parameters parameters = codeSystemVCParameters(url, code, version, display, displayLanguage, formatOrg(org)); + return handleFhirOperation(parameters, CodeSystem.class, VALIDATE_CODE); + } + + @PostMapping(path = {"/{org}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateCodeSystemsByOrg(@PathVariable String org, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); + return handleFhirOperation(params, CodeSystem.class, VALIDATE_CODE); + } + + @GetMapping(path = {"/{org}/ValueSet/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getValueSetByOrg(@PathVariable String org, + @PathVariable String id, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id, PAGE, page); + return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id); + } + + @GetMapping(path = {"/{org}/ValueSet/{id}/version", + "/{org}/ValueSet/{id}/version/{version}"}, + produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getValueSetVersionsByOrg(@PathVariable(name = ORG) String org, + @PathVariable(name = ID) String id, + @PathVariable(name = VERSION) Optional version, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL), PAGE, page); + return handleSearchResource(ValueSet.class, OWNER, formatOrg(org), ID, id, VERSION, version.orElse(ALL)); + } + + @GetMapping(path = {"/{org}/ValueSet"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity searchValueSetsByOrg(@PathVariable String org) { + return handleSearchResource(ValueSet.class, OWNER, formatOrg(org)); + } + + @GetMapping(path = {"/{org}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateValueSetByOrg(@PathVariable String org, + @RequestParam(name = URL) String url, + @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, + @RequestParam(name = CODE) String code, + @RequestParam(name = SYSTEM) String system, + @RequestParam(name = SYSTEM_VERSION, required = false) String systemVersion, + @RequestParam(name = DISPLAY, required = false) String display, + @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { + + Parameters parameters = valueSetVCParameters(url, EMPTY, valueSetVersion, code, system, systemVersion, display, + displayLanguage, formatOrg(org)); + return handleFhirOperation(parameters, ValueSet.class, VALIDATE_CODE); + } + + @PostMapping(path = {"/{org}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateValueSetByOrg(@PathVariable String org, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); + return handleFhirOperation(params, ValueSet.class, VALIDATE_CODE); + } + + @GetMapping(path = {"/{org}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity expandValueSetByOrg(@PathVariable String org, + @RequestParam(name = URL) String url, + @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, + @RequestParam(name = OFFSET, required = false, defaultValue = "0") Integer offset, + @RequestParam(name = COUNT, required = false, defaultValue = "100") Integer count, + @RequestParam(name = INCLUDE_DESIGNATIONS, defaultValue = "true") Boolean includeDesignations, + @RequestParam(name = INCLUDE_DEFINITION, defaultValue = "false") Boolean includeDefinition, + @RequestParam(name = ACTIVE_ONLY, defaultValue = "true") Boolean activeOnly, + @RequestParam(name = DISPLAY_LANGUAGE, required = false) String displayLanguage, + @RequestParam(name = FILTER, required = false) String filter) { + + Parameters parameters = valueSetExpandParameters(url, valueSetVersion, offset, count, includeDesignations, + includeDefinition, activeOnly, displayLanguage, filter, formatOrg(org)); + return handleFhirOperation(parameters, ValueSet.class, EXPAND); + } + + @PostMapping(path = {"/{org}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity expandValueSetByOrg(@PathVariable String org, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatOrg(org))); + return handleFhirOperation(params, ValueSet.class, EXPAND); + } +} + diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirUserController.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirUserController.java new file mode 100644 index 0000000..39cf1de --- /dev/null +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/controller/OclFhirUserController.java @@ -0,0 +1,185 @@ +package org.openconceptlab.fhir.controller; + +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.ValueSet; +import org.openconceptlab.fhir.util.OclFhirUtil; +import org.openconceptlab.fhir.provider.CodeSystemResourceProvider; +import org.openconceptlab.fhir.provider.ValueSetResourceProvider; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +import static org.openconceptlab.fhir.util.OclFhirUtil.*; +import static org.openconceptlab.fhir.util.OclFhirConstants.*; +import static org.openconceptlab.fhir.util.OclFhirConstants.CODESYSTEM; + +@RestController +@RequestMapping({"/users"}) +public class OclFhirUserController extends BaseOclFhirController{ + + public OclFhirUserController(CodeSystemResourceProvider codeSystemResourceProvider, + ValueSetResourceProvider valueSetResourceProvider, + OclFhirUtil oclFhirUtil) { + super(codeSystemResourceProvider, valueSetResourceProvider, oclFhirUtil); + } + + @PostMapping(path = {"/{user}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity createCodeSystemForUser(@PathVariable(name = USER) String user, + @RequestBody String codeSystem, + @RequestHeader(name = AUTHORIZATION) String auth) { + try { + CodeSystem system = (CodeSystem) parser.parseResource(codeSystem); + Optional acsnOpt = hasAccessionIdentifier(system.getIdentifier()); + ResponseEntity response = validate(user, system, acsnOpt, USERS, user); + if (response != null) return response; + if (acsnOpt.isEmpty()) addIdentifier(system.getIdentifier(), USERS, user, CODESYSTEM, system.getId()); + + performCreate(system, auth); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } catch (BaseServerResponseException e) { + return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBody()); + } catch (Exception e) { + return badRequest(); + } + } + + @GetMapping(path = {"/{user}/CodeSystem/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getCodeSystemByUser(@PathVariable String user, + @PathVariable String id, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id, PAGE, page); + return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id); + } + + @GetMapping(path = {"/{user}/CodeSystem/{id}/version", + "/{user}/CodeSystem/{id}/version/{version}"}, + produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getCodeSystemVersionsByUser(@PathVariable(name = USER) String user, + @PathVariable(name = ID) String id, + @PathVariable(name = VERSION) Optional version, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL), PAGE, page); + return handleSearchResource(CodeSystem.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL)); + } + + @GetMapping(path = {"/{user}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity searchCodeSystemsByUser(@PathVariable String user) { + return handleSearchResource(CodeSystem.class, OWNER, formatUser(user)); + } + + @GetMapping(path = {"/{user}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity lookUpCodeSystemsByUser(@PathVariable String user, + @RequestParam(name = SYSTEM) String system, + @RequestParam(name = CODE) String code, + @RequestParam(name = VERSION, required = false) String version, + @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { + Parameters parameters = lookupParameters(system, code, version, displayLanguage, formatUser(user)); + return handleFhirOperation(parameters, CodeSystem.class, LOOKUP); + } + + @PostMapping(path = {"/{user}/CodeSystem/$lookup"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity lookUpCodeSystemsByUser(@PathVariable String user, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); + return handleFhirOperation(params, CodeSystem.class, LOOKUP); + } + + @GetMapping(path = {"/{user}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateCodeSystemsByUser(@PathVariable String user, + @RequestParam(name = URL) String url, + @RequestParam(name = CODE) String code, + @RequestParam(name = VERSION, required = false) String version, + @RequestParam(name = DISPLAY, required = false) String display, + @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { + Parameters parameters = codeSystemVCParameters(url, code, version, display, displayLanguage, formatUser(user)); + return handleFhirOperation(parameters, CodeSystem.class, VALIDATE_CODE); + } + + @PostMapping(path = {"/{user}/CodeSystem/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateCodeSystemsByUser(@PathVariable String user, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); + return handleFhirOperation(params, CodeSystem.class, VALIDATE_CODE); + } + + @GetMapping(path = {"/{user}/ValueSet/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getValueSetByUser(@PathVariable String user, + @PathVariable String id, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id, PAGE, page); + return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id); + } + + @GetMapping(path = {"/{user}/ValueSet/{id}/version", + "/{user}/ValueSet/{id}/version/{version}"}, + produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity getValueSetVersionsByUser(@PathVariable(name = USER) String user, + @PathVariable(name = ID) String id, + @PathVariable(name = VERSION) Optional version, + @RequestParam(name = PAGE, required = false) String page) { + if (isValid(page)) + return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL), PAGE, page); + return handleSearchResource(ValueSet.class, OWNER, formatUser(user), ID, id, VERSION, version.orElse(ALL)); + } + + @GetMapping(path = {"/{user}/ValueSet"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity searchValueSetsByUser(@PathVariable String user) { + return handleSearchResource(ValueSet.class, OWNER, formatUser(user)); + } + + @GetMapping(path = {"/{user}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateValueSetByUser(@PathVariable String user, + @RequestParam(name = URL) String url, + @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, + @RequestParam(name = CODE) String code, + @RequestParam(name = SYSTEM) String system, + @RequestParam(name = SYSTEM_VERSION, required = false) String systemVersion, + @RequestParam(name = DISPLAY, required = false) String display, + @RequestParam(name = DISP_LANG, required = false) String displayLanguage) { + + Parameters parameters = valueSetVCParameters(url, EMPTY, valueSetVersion, code, system, systemVersion, display, + displayLanguage, formatUser(user)); + return handleFhirOperation(parameters, ValueSet.class, VALIDATE_CODE); + } + + @PostMapping(path = {"/{user}/ValueSet/$validate-code"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity validateValueSetByUser(@PathVariable String user, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); + return handleFhirOperation(params, ValueSet.class, VALIDATE_CODE); + } + + @GetMapping(path = {"/{user}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity expandValueSetByUser(@PathVariable String user, + @RequestParam(name = URL) String url, + @RequestParam(name = VALUESET_VERSION, required = false) String valueSetVersion, + @RequestParam(name = OFFSET, required = false, defaultValue = "0") Integer offset, + @RequestParam(name = COUNT, required = false, defaultValue = "100") Integer count, + @RequestParam(name = INCLUDE_DESIGNATIONS, defaultValue = "true") Boolean includeDesignations, + @RequestParam(name = INCLUDE_DEFINITION, defaultValue = "false") Boolean includeDefinition, + @RequestParam(name = ACTIVE_ONLY, defaultValue = "true") Boolean activeOnly, + @RequestParam(name = DISPLAY_LANGUAGE, required = false) String displayLanguage, + @RequestParam(name = FILTER, required = false) String filter) { + + Parameters parameters = valueSetExpandParameters(url, valueSetVersion, offset, count, includeDesignations, + includeDefinition, activeOnly, displayLanguage, filter, formatUser(user)); + return handleFhirOperation(parameters, ValueSet.class, EXPAND); + } + + @PostMapping(path = {"/{user}/ValueSet/$expand"}, produces = {MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity expandValueSetByUser(@PathVariable String user, @RequestBody String parameters){ + Parameters params = (Parameters) getResource(parameters); + params.addParameter().setName(OWNER).setValue(newStringType(formatUser(user))); + return handleFhirOperation(params, ValueSet.class, EXPAND); + } + +} diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/BaseConverter.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/BaseConverter.java new file mode 100644 index 0000000..39fc07b --- /dev/null +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/BaseConverter.java @@ -0,0 +1,321 @@ +package org.openconceptlab.fhir.converter; + +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.CodeSystem; +import org.openconceptlab.fhir.model.*; +import org.openconceptlab.fhir.repository.*; +import org.openconceptlab.fhir.util.OclFhirUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.sql.DataSource; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.*; +import java.util.stream.Collectors; + +import static org.openconceptlab.fhir.util.OclFhirConstants.*; +import static org.openconceptlab.fhir.util.OclFhirUtil.*; +import static org.openconceptlab.fhir.util.OclFhirUtil.gson; + +@Component +public class BaseConverter { + + protected SourceRepository sourceRepository; + protected ConceptRepository conceptRepository; + protected OclFhirUtil oclFhirUtil; + protected UserProfile oclUser; + protected ConceptsSourceRepository conceptsSourceRepository; + protected AuthtokenRepository authtokenRepository; + protected UserProfilesOrganizationRepository userProfilesOrganizationRepository; + protected OrganizationRepository organizationRepository; + protected UserRepository userRepository; + protected SimpleJdbcInsert insertLocalizedText; + protected SimpleJdbcInsert insertConcept; + protected DataSource dataSource; + protected JdbcTemplate jdbcTemplate; + + protected static final String insertConceptNamesSql = "insert into concepts_names (localizedtext_id,concept_id) values (?,?)"; + protected static final String insertConceptDescSql = "insert into concepts_descriptions (localizedtext_id,concept_id) values (?,?)"; + protected static final String updateConceptVersionSql = "update concepts set version = ? where id = ?"; + protected static final String insertConceptsSources = "insert into concepts_sources (concept_id,source_id) values (?,?)"; + + @Autowired + public BaseConverter(SourceRepository sourceRepository, ConceptRepository conceptRepository, OclFhirUtil oclFhirUtil, + UserProfile oclUser, ConceptsSourceRepository conceptsSourceRepository, DataSource dataSource, + AuthtokenRepository authtokenRepository, UserProfilesOrganizationRepository userProfilesOrganizationRepository, + OrganizationRepository organizationRepository, UserRepository userRepository) { + this.sourceRepository = sourceRepository; + this.conceptRepository = conceptRepository; + this.oclFhirUtil = oclFhirUtil; + this.oclUser = oclUser; + this.conceptsSourceRepository = conceptsSourceRepository; + this.dataSource = dataSource; + this.authtokenRepository = authtokenRepository; + this.userProfilesOrganizationRepository = userProfilesOrganizationRepository; + this.organizationRepository = organizationRepository; + this.userRepository = userRepository; + } + + @PostConstruct + public void init() { + this.jdbcTemplate = new JdbcTemplate(dataSource); + insertLocalizedText = new SimpleJdbcInsert(jdbcTemplate).withTableName("localized_texts"); + insertConcept = new SimpleJdbcInsert(jdbcTemplate).withTableName("concepts"); + } + + protected BaseOclEntity validateOwner(String org, String username) { + if (isValid(org)) { + Organization organization = organizationRepository.findByMnemonic(org); + if (organization == null) { + throw new InvalidRequestException("The organization of id = " + org + " does not exist."); + } else { + return organization; + } + } else { + UserProfile userProfile = userRepository.findByUsername(username); + if (userProfile == null) { + throw new InvalidRequestException("The user of username = " + username + " does not exist."); + } else { + return userProfile; + } + } + } + + protected AuthtokenToken validateToken(String authToken) { + if (isValid(authToken)) { + String tokenStr = authToken.replaceAll("Token\\s+", EMPTY); + return authtokenRepository.findByKey(tokenStr.trim()); + } else { + throw new AuthenticationException("The authentication token is not provided."); + } + } + + protected void authenticate(AuthtokenToken token, String username, String org) { + if (token == null) { + throw new AuthenticationException("Invalid authentication token."); + } + if (isValid(username)) { + if (!username.equals(token.getUserProfile().getUsername())) { + throw new AuthenticationException("The " + username + " is not authorized to use the token provided."); + } + } else if (isValid(org)) { + boolean isMember = userProfilesOrganizationRepository.findByOrganizationMnemonic(org) + .stream() + .map(UserProfilesOrganization::getUserProfile) + .anyMatch(f -> f.getUsername().equals(token.getUserProfile().getUsername()) + && f.getAuthtokenTokens().stream().anyMatch(t -> t.getKey().equals(token.getKey()))); + if (!isMember) { + throw new AuthenticationException("The user " + token.getUserProfile().getUsername() + " is not authorized to access " + + org + " organization."); + } + } else { + throw new InvalidRequestException("Owner can not be empty."); + } + } + + protected void validateId(String username, String org, String id, String version, String resourceType) { + if (CODESYSTEM.equals(resourceType)) { + Source userSource = sourceRepository.findFirstByMnemonicAndVersionAndUserIdUsername(id, version, username); + Source orgSource = sourceRepository.findFirstByMnemonicAndVersionAndOrganizationMnemonic(id, version, org); + if (userSource != null || orgSource != null) { + throw new ResourceVersionConflictException(String.format("The %s %s of version %s already exists.", resourceType, id, version)); + } + } else { + throw new InternalErrorException("Invalid resource type."); + } + } + + protected void validateCanonicalUrl(String username, String org, String url, String version, String resourceType) { + if (CODESYSTEM.equals(resourceType)) { + Source userSource = sourceRepository.findFirstByCanonicalUrlAndVersionAndUserIdUsername(url, version, username); + Source orgSource = sourceRepository.findFirstByCanonicalUrlAndVersionAndOrganizationMnemonic(url, version, org); + if (userSource != null || orgSource != null) { + throw new ResourceVersionConflictException(String.format("The %s of canonical url %s and version %s already exists.", resourceType, url, version)); + } + } else { + throw new InternalErrorException("Invalid resource type."); + } + } + + protected void addJsonStrings(final CodeSystem codeSystem, final Source source) { + source.setIdentifier(convertToJsonString(getResIdentifierString(codeSystem), IDENTIFIER)); + if (!codeSystem.getContact().isEmpty()) + source.setContact(convertToJsonString(getResContactString(codeSystem), CONTACT)); + if (!codeSystem.getJurisdiction().isEmpty()) + source.setJurisdiction(convertToJsonString(getResJurisdictionString(codeSystem), JURISDICTION)); + } + + protected void batchUpdateConceptVersion(List conceptIds) { + this.jdbcTemplate.batchUpdate(updateConceptVersionSql, new BatchPreparedStatementSetter() { + public void setValues(PreparedStatement ps, int i) + throws SQLException { + ps.setInt(1, conceptIds.get(i)); + ps.setInt(2, conceptIds.get(i)); + } + public int getBatchSize() { + return conceptIds.size(); + } + }); + } + + protected void batchUpdateConceptSources(List conceptIds, Integer sourceId) { + this.jdbcTemplate.batchUpdate(insertConceptsSources, new BatchPreparedStatementSetter() { + public void setValues(PreparedStatement ps, int i) + throws SQLException { + ps.setInt(1, conceptIds.get(i)); + ps.setInt(2, sourceId); + } + public int getBatchSize() { + return conceptIds.size(); + } + }); + } + + protected void batchInsertConceptNames(String sql, List nameIds, Integer conceptId) { + this.jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { + public void setValues(PreparedStatement ps, int i) + throws SQLException { + ps.setInt(1, nameIds.get(i).intValue()); + ps.setInt(2, conceptId); + } + public int getBatchSize() { + return nameIds.size(); + } + }); + } + + protected void batchConcepts(List concepts, List conceptIds) { + concepts.forEach(c -> { + Integer conceptId = insert(insertConcept, toMap(c)).intValue(); + if (!c.getConceptsNames().isEmpty()) { + List nameIds = insertRows( + c.getConceptsNames().stream().filter(Objects::nonNull).filter(f -> f.getLocalizedText() != null).map(ConceptsName::getLocalizedText).collect(Collectors.toList()) + ); + batchInsertConceptNames(insertConceptNamesSql, nameIds, conceptId); + } + if (!c.getConceptsDescriptions().isEmpty()) { + List descIds = insertRows( + c.getConceptsDescriptions().stream().filter(Objects::nonNull).filter(f -> f.getLocalizedText() != null).map(ConceptsDescription::getLocalizedText).collect(Collectors.toList()) + ); + batchInsertConceptNames(insertConceptDescSql, descIds, conceptId); + } + conceptIds.add(conceptId); + }); + } + + protected List insertRows(List texts) { + List keys = new ArrayList<>(); + texts.forEach(t -> { + keys.add(insert(insertLocalizedText, toMap(t))); + }); + return keys; + } + + private Map toMap(LocalizedText text) { + Map map = new HashMap<>(); + map.put(NAME, text.getName()); + map.put(TYPE, text.getType()); + map.put(LOCALE, text.getLocale()); + map.put(LOCALE_PREFERRED, text.getLocalePreferred()); + map.put(CREATED_AT, text.getCreatedAt()); + return map; + } + + private Map toMap(Concept obj) { + Map map = new HashMap<>(); + map.put(PUBLIC_ACCESS, obj.getPublicAccess()); + map.put(IS_ACTIVE, obj.getIsActive()); + map.put(EXTRAS, obj.getExtras()); + map.put(URI, obj.getUri()); + map.put(MNEMONIC, obj.getMnemonic()); + map.put(VERSION, obj.getVersion()); + map.put(RELEASED, obj.getReleased()); + map.put(RETIRED, obj.getRetired()); + map.put(IS_LATEST_VERSION, obj.getIsLatestVersion()); + map.put(NAME, obj.getName()); + map.put(FULL_NAME, obj.getFullName()); + map.put(DEFAULT_LOCALE, obj.getDefaultLocale()); + map.put(CONCEPT_CLASS, obj.getConceptClass()); + map.put(DATATYPE, obj.getDatatype()); + map.put(COMMENT, obj.getComment()); + map.put(CREATED_BY_ID, obj.getCreatedBy().getId()); + map.put(UPDATED_BY_ID, obj.getUpdatedBy().getId()); + map.put(PARENT_ID, obj.getParent().getId()); + map.put(CREATED_AT, obj.getParent().getCreatedAt()); + map.put(UPDATED_AT, obj.getParent().getUpdatedAt()); + return map; + } + + private Long insert(SimpleJdbcInsert insert, Map parameters) { + if (!insert.isCompiled()) + insert.usingGeneratedKeyColumns("id"); + Number n = insert.executeAndReturnKeyHolder(parameters).getKey(); + if (n instanceof Long) + return n.longValue(); + if (n instanceof Integer) + return Long.valueOf(String.valueOf(n.intValue())); + return (Long) insert.executeAndReturnKeyHolder(parameters).getKey(); + } + + private String getResContactString(final CodeSystem codeSystem) { + CodeSystem system = new CodeSystem(); + system.setContact(codeSystem.getContact()); + return getFhirContext().newJsonParser().encodeResourceToString(system); + } + + private String getResIdentifierString(final CodeSystem codeSystem) { + CodeSystem system = new CodeSystem(); + system.setIdentifier(codeSystem.getIdentifier()); + return getFhirContext().newJsonParser().encodeResourceToString(system); + } + + private String getResJurisdictionString(final CodeSystem codeSystem) { + CodeSystem system = new CodeSystem(); + system.setJurisdiction(codeSystem.getJurisdiction()); + return getFhirContext().newJsonParser().encodeResourceToString(system); + } + + private String convertToJsonString(String fhirResourceStr, String key) { + JsonObject object = jsonParser.parse(fhirResourceStr).getAsJsonObject(); + if (object.has(RESOURCE_TYPE)) + object.remove(RESOURCE_TYPE); + if (object.has(key)) { + if (object.get(key) instanceof JsonArray) { + return gson.toJson(object.getAsJsonArray(key)); + } else { + return gson.toJson(object.getAsJsonObject(key)); + } + } + return EMPTY_JSON; + } + + protected String getStringProperty(List properties, String property) { + Optional component = properties.parallelStream().filter(p -> property.equals(p.getCode())).findAny(); + if (component.isPresent() && isValid(component.get().getValueStringType().getValue())) + return component.get().getValueStringType().getValue(); + return NA; + } + + protected boolean getBooleanProperty(List properties, String property) { + Optional component = properties.parallelStream().filter(p -> property.equals(p.getCode())).findAny(); + if (component.isPresent()) { + BooleanType value = component.get().getValueBooleanType(); + if(value.getValue() != null) return value.getValue(); + } + return false; + } + +} + diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/CodeSystemConverter.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/CodeSystemConverter.java index a2cd2b2..79d0838 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/CodeSystemConverter.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/CodeSystemConverter.java @@ -1,52 +1,49 @@ package org.openconceptlab.fhir.converter; import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import com.google.gson.*; +import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.r4.model.CodeSystem.CodeSystemFilterComponent; import org.hl7.fhir.r4.model.CodeSystem.ConceptPropertyComponent; -import org.hl7.fhir.r4.model.CodeSystem.FilterOperator; +import org.hl7.fhir.r4.model.codesystems.PublicationStatus; import org.openconceptlab.fhir.model.*; import static org.openconceptlab.fhir.util.OclFhirConstants.*; import static org.openconceptlab.fhir.util.OclFhirUtil.*; -import org.openconceptlab.fhir.repository.ConceptRepository; -import org.openconceptlab.fhir.repository.ConceptsSourceRepository; -import org.openconceptlab.fhir.repository.SourceRepository; +import org.openconceptlab.fhir.model.Organization; +import org.openconceptlab.fhir.repository.*; +import org.openconceptlab.fhir.util.OclFhirConstants; import org.openconceptlab.fhir.util.OclFhirUtil; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Component; +import javax.sql.DataSource; + /** * The CodeSystemConverter. * @author harpatel1 */ @Component -public class CodeSystemConverter { - - JsonParser parser = new JsonParser(); - - SourceRepository sourceRepository; - ConceptRepository conceptRepository; - OclFhirUtil oclFhirUtil; - UserProfile oclUser; - ConceptsSourceRepository conceptsSourceRepository; - - @Autowired - public CodeSystemConverter(SourceRepository sourceRepository, ConceptRepository conceptRepository, OclFhirUtil oclFhirUtil - , UserProfile oclUser, ConceptsSourceRepository conceptsSourceRepository) { - this.sourceRepository = sourceRepository; - this.conceptRepository = conceptRepository; - this.oclFhirUtil = oclFhirUtil; - this.oclUser = oclUser; - this.conceptsSourceRepository = conceptsSourceRepository; +public class CodeSystemConverter extends BaseConverter { + + public static final String DEFAULT_SOURCE_VERSION = "0.1"; + private static final Log log = LogFactory.getLog(CodeSystemConverter.class); + public CodeSystemConverter(SourceRepository sourceRepository, ConceptRepository conceptRepository, OclFhirUtil oclFhirUtil, + UserProfile oclUser, ConceptsSourceRepository conceptsSourceRepository, DataSource dataSource, + AuthtokenRepository authtokenRepository, UserProfilesOrganizationRepository userProfilesOrganizationRepository, + OrganizationRepository organizationRepository, UserRepository userRepository) { + super(sourceRepository, conceptRepository, oclFhirUtil, oclUser, conceptsSourceRepository, dataSource, authtokenRepository, + userProfilesOrganizationRepository, organizationRepository, userRepository); } public List convertToCodeSystem(List sources, boolean includeConcepts, int page) { @@ -83,15 +80,14 @@ private CodeSystem toBaseCodeSystem(final Source source){ codeSystem.setTitle(source.getFullName()); } // status - addStatus(codeSystem, source.getIsActive(), source.getRetired() != null ? source.getRetired() : false, + addStatus(codeSystem, source.getRetired() != null ? source.getRetired() : false, source.getReleased() != null ? source.getReleased() : false); // language codeSystem.setLanguage(source.getDefaultLocale()); // description - if(StringUtils.isNotBlank(source.getDescription())) { + if(StringUtils.isNotBlank(source.getDescription())) codeSystem.setDescription(source.getDescription()); - } // count codeSystem.setCount(conceptRepository.findConceptCountInSource(source.getId())); // property @@ -101,7 +97,8 @@ private CodeSystem toBaseCodeSystem(final Source source){ codeSystem.setPublisher(source.getPublisher()); // override default identifier with database value // identifier, contact, jurisdiction - addJsonFields(codeSystem, isValid(source.getIdentifier()) && !"{}".equals(source.getIdentifier()) ? source.getIdentifier() : "", source.getContact(), source.getJurisdiction()); + addJsonFields(codeSystem, isValid(source.getIdentifier()) && !EMPTY_JSON.equals(source.getIdentifier()) ? source.getIdentifier() : EMPTY, + source.getContact(), source.getJurisdiction()); // purpose if (isValid(source.getPurpose())) codeSystem.setPurpose(source.getPurpose()); @@ -128,12 +125,12 @@ private CodeSystem toBaseCodeSystem(final Source source){ private void addProperty(final CodeSystem codeSystem) { // concept class - codeSystem.getProperty().add(getPropertyComponent(CONCEPT_CLASS, + codeSystem.getProperty().add(getPropertyComponent(OclFhirConstants.CONCEPTCLASS, SYSTEM_CC, DESC_CC, CodeSystem.PropertyType.STRING)); // data type - codeSystem.getProperty().add(getPropertyComponent(DATATYPE, + codeSystem.getProperty().add(getPropertyComponent(OclFhirConstants.DATATYPE, SYSTEM_DT, DESC_DT, CodeSystem.PropertyType.STRING)); @@ -168,7 +165,7 @@ private void addConceptsToCodeSystem(final CodeSystem codeSystem, final Source s // definition List definitions = concept.getConceptsDescriptions().stream() .filter(c -> c.getLocalizedText() != null) - .filter(c -> isValid(c.getLocalizedText().getType()) && "definition".equalsIgnoreCase(c.getLocalizedText().getType())) + .filter(c -> isValid(c.getLocalizedText().getType()) && DEFINITION.equalsIgnoreCase(c.getLocalizedText().getType())) .map(ConceptsDescription::getLocalizedText) .collect(Collectors.toList()); Optional definition = oclFhirUtil.getDisplayForLanguage(definitions, source.getDefaultLocale()); @@ -178,9 +175,9 @@ private void addConceptsToCodeSystem(final CodeSystem codeSystem, final Source s addConceptDesignation(concept, definitionComponent); // property - concept_class, data_type, ,inactive - definitionComponent.getProperty().add(new ConceptPropertyComponent(new CodeType(CONCEPT_CLASS), + definitionComponent.getProperty().add(new ConceptPropertyComponent(new CodeType(OclFhirConstants.CONCEPTCLASS), new StringType(concept.getConceptClass()))); - definitionComponent.getProperty().add(new ConceptPropertyComponent(new CodeType(DATATYPE), + definitionComponent.getProperty().add(new ConceptPropertyComponent(new CodeType(OclFhirConstants.DATATYPE), new StringType(concept.getDatatype()))); definitionComponent.getProperty().add(new ConceptPropertyComponent(new CodeType(INACTIVE), new BooleanType(concept.getRetired()))); @@ -189,64 +186,13 @@ private void addConceptsToCodeSystem(final CodeSystem codeSystem, final Source s } } - private void addExtras(CodeSystem codeSystem, String extras) { - if(StringUtils.isNotBlank(extras)) { - JsonObject obj = parseExtras(extras); - // filters - JsonArray filters = obj.getAsJsonArray(FILTERS); - if (filters != null && filters.isJsonArray()) { - for(JsonElement je : filters) { - JsonObject filter = je.getAsJsonObject(); - if(filter.get(CODE) != null) { - String code = filter.get(CODE).getAsString(); - if(CONCEPT_CLASS.equals(code)) { - addFilter(codeSystem, CONCEPT_CLASS, filter.get(DESC), filter.get(OPERATOR), - filter.get(VALUE)); - } else if (DATATYPE.equals(code)) { - addFilter(codeSystem, DATATYPE, filter.get(DESC), filter.get(OPERATOR), - filter.get(VALUE)); - } - } - } - } - // identifier - JsonArray identifiers = obj.getAsJsonArray(IDENTIFIERS); - codeSystem.setIdentifier(getIdentifiers(identifiers)); - - // purpose - if(isValidElement(obj.get(PURPOSE))) - codeSystem.setPurpose(obj.get(PURPOSE).getAsString()); - // copyright - if(isValidElement(obj.get(COPYRIGHT))) - codeSystem.setCopyright(obj.get(COPYRIGHT).getAsString()); - } - } - - private void addFilter(CodeSystem codeSystem, String code, JsonElement description, JsonElement operator, - JsonElement value) { - if(StringUtils.isNotBlank(code)) { - CodeSystemFilterComponent c = new CodeSystemFilterComponent(); - c.setCode(code); - if (description != null && description.getAsString() != null) - c.setDescription(description.getAsString()); - if (operator != null && operator.getAsString() != null) { - if(allowedFilterOperators.contains(operator.getAsString())) { - c.addOperator(FilterOperator.fromCode(operator.getAsString())); - } - } - if (value != null && value.getAsString() != null) - c.setValue(value.getAsString()); - codeSystem.getFilter().add(c); - } - } - public Parameters getLookupParameters(final Source source, final CodeType code, final CodeType displayLanguage) { Optional conceptOpt = oclFhirUtil.getSourceConcept(source, code.getCode(), EMPTY); if (conceptOpt.isPresent()) { Concept concept = conceptOpt.get(); Parameters parameters = new Parameters(); - parameters.addParameter(getParameter(NAME, source.getName())); - parameters.addParameter(getParameter(VERSION, source.getVersion())); + parameters.addParameter(getParameter(OclFhirConstants.NAME, source.getName())); + parameters.addParameter(getParameter(OclFhirConstants.VERSION, source.getVersion())); List names = oclFhirUtil.getNames(concept); getDisplayForLookUp(names, isValid(displayLanguage) ? displayLanguage.getCode() : EMPTY, source.getDefaultLocale()) .ifPresent(display -> parameters.addParameter(getParameter(DISPLAY, display))); @@ -327,73 +273,251 @@ private Parameters.ParametersParameterComponent getParameter(String name, String return component; } - /** - * TODO: Update when ready to implement POST - * @param codeSystem - */ - /* - public void validateCodeSystem(CodeSystem codeSystem) { - Optional id = codeSystem.getIdentifier().stream().filter(i -> i.getType().hasCoding("http://hl7.org/fhir/v2/0203", "ACSN")) - .filter(i -> "http://fhir.openconceptlab.org".equals(i.getSystem())) - .filter(i -> isValid(i.getValue())) - .findFirst(); - String url = codeSystem.getUrl(); - if (!id.isPresent() && !isValid(url)) { - throw new UnprocessableEntityException("CodeSystem must have either Identifier or URL value."); + public void createCodeSystem(CodeSystem codeSystem, String accessionId, String authToken) { + String org = EMPTY; + String username = EMPTY; + String codeSystemId = EMPTY; + String formattedId = formatExpression(accessionId); + String [] ar = formattedId.split(FW_SLASH); + if (ar.length >= 5) { + if (ORGS.equals(ar[1]) && isValid(ar[2])) { + org = ar[2]; + } else if (USERS.equals(ar[1]) && isValid(ar[2])){ + username = ar[2]; + } + if (CODESYSTEM.equals(ar[3]) && isValid(ar[4])) { + codeSystemId = ar[4]; + codeSystem.setId(codeSystemId); + } + if (ar.length >=6 && isValid(ar[5])) { + codeSystem.setVersion(ar[5]); + } else if (!isValid(codeSystem.getVersion())) { + codeSystem.setVersion(DEFAULT_SOURCE_VERSION); + formattedId = formattedId + DEFAULT_SOURCE_VERSION + FW_SLASH; + } else { + formattedId = formattedId + codeSystem.getVersion() + FW_SLASH; + } } - // check for unique id - if(id.isPresent()) { - if (!sourceRepository.findByMnemonicAndPublicAccessIn(id.get().getValue(), publicAccess).isEmpty()) { - throw new UnprocessableEntityException(String.format("The CodeSystem of Id '%s' already exists", id.get().getValue())); + + if (org.isEmpty() && username.isEmpty()) + throw new InvalidRequestException("Owner type and id is required."); + if (codeSystemId.isEmpty()) + throw new InvalidRequestException("CodeSystem id is required."); + + BaseOclEntity owner = validateOwner(org, username); + AuthtokenToken token = validateToken(authToken); + authenticate(token, username, org); + validateId(username, org, codeSystemId, codeSystem.getVersion(), CODESYSTEM); + validateCanonicalUrl(username, org, codeSystem.getUrl(), codeSystem.getVersion(), CODESYSTEM); + + UserProfile user = token.getUserProfile(); + // base source + Source source = toBaseSource(codeSystem, user, formattedId); + // add parent and access + addParent(source, owner); + // add identifier, contact and jurisdiction + addJsonStrings(codeSystem, source); + // version-less source uri + String value = source.getUri().substring(0, source.getUri().lastIndexOf(FW_SLASH)); + String versionLessSourceUri = value.substring(0, value.lastIndexOf(FW_SLASH)) + FW_SLASH; + // add concepts + List concepts = toConcepts(codeSystem.getConcept(), codeSystem.getLanguage()); + concepts.forEach(c -> { + c.setParent(source); + c.setPublicAccess(source.getPublicAccess()); + c.setVersion("1"); + c.setIsLatestVersion(true); + c.setReleased(c.getIsActive()); + c.setRetired(c.getIsActive()); + c.setDefaultLocale(source.getDefaultLocale()); + c.setCreatedBy(user); + c.setUpdatedBy(user); + c.setVersionedObject(c); + c.setUri(versionLessSourceUri + "concepts/" + c.getMnemonic() + FW_SLASH + c.getVersion() + FW_SLASH); + c.setExtras(EMPTY_JSON); + }); + + // save source + sourceRepository.saveAndFlush(source); + log.info("saved source - " + source.getMnemonic()); + + // save concepts + List conceptIds = new CopyOnWriteArrayList<>(); + // concept, concept names, concept descriptions, localized texts + List> conceptBatches = ListUtils.partition(concepts, 1000); + int i = 1; + for (List cb: conceptBatches) { + log.info("Saving " + cb.size() + " concepts, batch " + i + " of " + conceptBatches.size()); + batchConcepts(cb, conceptIds); + i++; + } + + // update concept version = concept id + List> conceptIdBatches = ListUtils.partition(conceptIds, 1000); + conceptIdBatches.forEach(this::batchUpdateConceptVersion); + + // save concepts sources + conceptIdBatches.forEach(b -> batchUpdateConceptSources(b, source.getId().intValue())); + log.info("saved " + conceptIds.size() + " concepts"); + } + + private List toConcepts(List components, String defaultLocale) { + List concepts = new ArrayList<>(); + for (CodeSystem.ConceptDefinitionComponent component : components) { + Concept concept = toConcept(component, defaultLocale); + if (concept != null) concepts.add(concept); + } + return concepts; + } + + private Concept toConcept(CodeSystem.ConceptDefinitionComponent component, String defaultLocale) { + Concept concept = new Concept(); + String code = component.getCode(); + if (isValid(code)) { + // code + concept.setMnemonic(code); + // name + concept.setName(code); + // definition + addDefinition(concept, component.getDefinition(), defaultLocale); + // designation + List designationComponents = component.getDesignation(); + List names = toLocalizedText(designationComponents, defaultLocale); + if (!names.isEmpty()) { + addDesignation(concept, names); } - codeSystem.setId(id.get().getValue()); + // property + List properties = component.getProperty(); + // -- concept class + concept.setConceptClass(getStringProperty(properties, OclFhirConstants.CONCEPT_CLASS)); + // -- data type + concept.setDatatype(getStringProperty(properties, OclFhirConstants.DATATYPE)); + // -- inactive + concept.setIsActive(!getBooleanProperty(properties, INACTIVE)); + return concept; } - // check for unique URL - if(isValid(url) && !sourceRepository.findByCanonicalUrlAndPublicAccessIn(url, publicAccess).isEmpty()) - throw new UnprocessableEntityException(String.format("The CodeSystem of URL '%s' already exists", url)); + return null; + } - // check publisher - validatePublisher(codeSystem.getPublisher()); + private void addDefinition(Concept concept, String definition, String defaultLocale) { + if (isValid(definition)) { + LocalizedText text = new LocalizedText(); + text.setName(definition); + text.setType(DEFINITION); + text.setLocale(defaultLocale); + text.setLocalePreferred(true); + + ConceptsDescription description = new ConceptsDescription(); + description.setConcept(concept); + description.setLocalizedText(text); + concept.getConceptsDescriptions().add(description); + } + } - // meta.updatedAt = source.updated_at - toSource(codeSystem); + private void addDesignation(Concept concept, List names) { + List conceptsNames = names.stream().map(name -> { + ConceptsName conceptsName = new ConceptsName(); + conceptsName.setConcept(concept); + conceptsName.setLocalizedText(name); + return conceptsName; + }).collect(Collectors.toList()); + concept.setConceptsNames(conceptsNames); } - public void toSource(final CodeSystem codeSystem){ + private List toLocalizedText(List components, String defaultLocale) { + List texts = new ArrayList<>(); + for (CodeSystem.ConceptDefinitionDesignationComponent component : components) { + String locale = component.getLanguage(); + String type = component.getUse().getCode(); + String name = component.getValue(); + if (locale != null && name != null) { + LocalizedText text = new LocalizedText(); + text.setLocale(locale); + if (type != null) + text.setType(type); + text.setName(name); + text.setLocalePreferred(locale.equals(defaultLocale)); + texts.add(text); + } + } + return texts; + } + + private Source toBaseSource(final CodeSystem codeSystem, final UserProfile user, final String uri) { Source source = new Source(); + // mnemonic source.setMnemonic(codeSystem.getId()); + // canonical url source.setCanonicalUrl(codeSystem.getUrl()); - source.setCreatedBy(new UserProfile(oclUser.getId())); - source.setUpdatedBy(new UserProfile(oclUser.getId())); - source.setIsActive(PublicationStatus.ACTIVE.equals(codeSystem.getStatus())); - source.setRetired(PublicationStatus.RETIRED.equals(codeSystem.getStatus())); - if (isValid(codeSystem.getVersion())) source.setVersion(codeSystem.getVersion()); - if (isValid(codeSystem.getName())) source.setName(codeSystem.getName()); - if (isValid(codeSystem.getLanguage())) source.setDefaultLocale(codeSystem.getLanguage()); - source.setActiveConcepts(codeSystem.getCount() == 0 ? codeSystem.getConcept().size() : codeSystem.getCount()); - source.setExtras(getExtras(codeSystem)); - if (isValid(source.getMnemonic())) { - sourceRepository.save(source); - } else { - source.setMnemonic("TEMP"); - Source temp = sourceRepository.save(source); - sourceRepository.updateMnemonic(temp.getId()); + // created by + source.setCreatedBy(user); + // updated by + source.setUpdatedBy(user); + + // draft or unknown or empty + source.setIsActive(true); + source.setIsLatestVersion(true); + source.setRetired(false); + source.setReleased(false); + if (codeSystem.getStatus() != null) { + // active + if (PublicationStatus.ACTIVE.toCode().equals(codeSystem.getStatus().toCode())) { + source.setReleased(true); + // retired + } else if (PublicationStatus.RETIRED.toCode().equals(codeSystem.getStatus().toCode())) { + source.setRetired(true); + source.setReleased(false); + source.setIsActive(false); + source.setIsLatestVersion(false); + } } - } - - private String getExtras(final CodeSystem codeSystem) { - CodeSystem system = codeSystem.copy(); - nullifyBaseFields(system); - return toFhirString(system); - } + // version + source.setVersion(codeSystem.getVersion()); + // default locale + source.setDefaultLocale(isValid(codeSystem.getLanguage()) ? codeSystem.getLanguage() : EN_LOCALE); + // uri + source.setUri(uri.replaceAll("(?i)"+ Pattern.quote(CODESYSTEM), "sources")); + // active concepts + source.setActiveConcepts(codeSystem.getConcept().size()); + // active mappings + source.setActiveMappings(0); + // name + String name = isValid(codeSystem.getName()) ? codeSystem.getName() : codeSystem.getId(); + source.setName(name); + // content type + if (codeSystem.getContent() != null) + source.setContentType(codeSystem.getContent().toCode()); + // copyright + if (isValid(codeSystem.getCopyright())) + source.setCopyright(codeSystem.getCopyright()); + // description + if (isValid(codeSystem.getDescription())) + source.setDescription(codeSystem.getDescription()); + // title + if (isValid(codeSystem.getTitle())) + source.setFullName(codeSystem.getTitle()); + // publisher + if (isValid(codeSystem.getPublisher())) + source.setPublisher(codeSystem.getPublisher()); + // purpose + if (isValid(codeSystem.getPurpose())) + source.setPurpose(codeSystem.getPurpose()); + // revision date + if (codeSystem.getDate() != null) + source.setRevisionDate(codeSystem.getDate()); + // extras + source.setExtras(EMPTY_JSON); + return source; + } - private void nullifyBaseFields(final CodeSystem codeSystem) { - codeSystem.setIdentifier(null); - codeSystem.setUrl(null); - codeSystem.setStatus(null); - codeSystem.setVersion(null); - codeSystem.setName(null); - codeSystem.setLanguage(null); + private void addParent(final Source source, final BaseOclEntity owner) { + if (owner instanceof Organization) { + Organization organization = (Organization) owner; + source.setOrganization(organization); + source.setPublicAccess(organization.getPublicAccess()); + } else if (owner instanceof UserProfile){ + source.setUserId((UserProfile) owner); + } } - */ } + diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/ValueSetConverter.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/ValueSetConverter.java index 059aeb4..8dab8c0 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/ValueSetConverter.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/converter/ValueSetConverter.java @@ -1,18 +1,18 @@ package org.openconceptlab.fhir.converter; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import com.google.gson.JsonParser; import org.hl7.fhir.r4.model.*; import org.openconceptlab.fhir.model.*; import org.openconceptlab.fhir.model.Collection; -import org.openconceptlab.fhir.repository.ConceptRepository; -import org.openconceptlab.fhir.repository.ConceptsSourceRepository; -import org.openconceptlab.fhir.repository.SourceRepository; +import org.openconceptlab.fhir.repository.*; import org.openconceptlab.fhir.util.OclFhirUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import javax.sql.DataSource; + import static org.openconceptlab.fhir.util.OclFhirConstants.*; import static org.openconceptlab.fhir.util.OclFhirUtil.*; import static org.openconceptlab.fhir.util.OclFhirUtil.getOwner; @@ -25,27 +25,19 @@ * @author harpatel1 */ @Component -public class ValueSetConverter { - - JsonParser parser = new JsonParser(); - - OclFhirUtil oclFhirUtil; - ConceptsSourceRepository conceptsSourceRepository; - ConceptRepository conceptRepository; - SourceRepository sourceRepository; - - @Autowired - public ValueSetConverter(OclFhirUtil oclFhirUtil, ConceptsSourceRepository conceptsSourceRepository, - ConceptRepository conceptRepository, SourceRepository sourceRepository) { - this.oclFhirUtil = oclFhirUtil; - this.conceptsSourceRepository = conceptsSourceRepository; - this.conceptRepository = conceptRepository; - this.sourceRepository = sourceRepository; - } +public class ValueSetConverter extends BaseConverter { @Value("${ocl.servlet.baseurl}") private String baseUrl; + public ValueSetConverter(SourceRepository sourceRepository, ConceptRepository conceptRepository, OclFhirUtil oclFhirUtil, + UserProfile oclUser, ConceptsSourceRepository conceptsSourceRepository, DataSource dataSource, + AuthtokenRepository authtokenRepository, UserProfilesOrganizationRepository userProfilesOrganizationRepository, + OrganizationRepository organizationRepository, UserRepository userRepository) { + super(sourceRepository, conceptRepository, oclFhirUtil, oclUser, conceptsSourceRepository, dataSource, authtokenRepository, + userProfilesOrganizationRepository, organizationRepository, userRepository); + } + public List convertToValueSet(List collections, Integer page) { List valueSets = new ArrayList<>(); collections.forEach(collection -> { @@ -78,7 +70,7 @@ private ValueSet toBaseValueSet(final Collection collection) { if(isValid(collection.getDescription())) valueSet.setDescription(collection.getDescription()); // set status - addStatus(valueSet, collection.getIsActive(), collection.getRetired() != null ? collection.getRetired() : false, + addStatus(valueSet, collection.getRetired() != null ? collection.getRetired() : false, collection.getReleased()); // publisher if (isValid(collection.getPublisher())) @@ -114,13 +106,6 @@ private Optional getConcept(List conceptsSources, Strin } } - private String formatExpression(String expression) { - String uri = expression; - if (!uri.startsWith("/")) uri = "/" + uri; - if (!uri.endsWith("/")) uri = uri + "/"; - return uri; - } - private void addCompose(ValueSet valueSet, Collection collection, boolean includeConceptDesignation, Integer page) { // We have to use expressions to determine actual Source version since its not possible through CollectionsConcepts IntegerType offset = new IntegerType(page * 100); @@ -160,7 +145,7 @@ private List getSourcesFromExpressions(List expressions, List getResourceType() { return CodeSystem.class; } + @Create + @Transactional + public MethodOutcome createCodeSystem(@ResourceParam CodeSystem codeSystem, RequestDetails requestDetails) { + if (codeSystem == null) { + throw new InvalidRequestException("The CodeSystem can not be empty"); + } + String accessionId = getAccessionIdentifier(codeSystem); + if (!isValid(accessionId)) { + throw new InvalidRequestException("The CodeSystem.identifier is empty or identifier of type ACSN is empty."); + } + if (!isValid(codeSystem.getUrl())) { + throw new InvalidRequestException("The CodeSystem.url can not be empty. Please provide canonical url."); + } + codeSystemConverter.createCodeSystem(codeSystem, accessionId, requestDetails.getHeader(AUTHORIZATION)); + return new MethodOutcome(); + } + /** * Returns all public {@link CodeSystem}. * diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/provider/ValueSetResourceProvider.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/provider/ValueSetResourceProvider.java index 64e79c9..e83f844 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/provider/ValueSetResourceProvider.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/provider/ValueSetResourceProvider.java @@ -3,15 +3,14 @@ import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.*; import org.openconceptlab.fhir.converter.ValueSetConverter; import org.openconceptlab.fhir.model.Collection; -import org.openconceptlab.fhir.model.Source; import org.openconceptlab.fhir.repository.CollectionRepository; import org.openconceptlab.fhir.util.OclFhirUtil; import static org.openconceptlab.fhir.util.OclFhirConstants.*; @@ -131,7 +130,7 @@ public Parameters valueSetValidateCode(@OperationParam(name = URL, type = UriTyp @OperationParam(name = OWNER, type = StringType.class) StringType owner) { if (isValid(code) && coding != null) - throw new UnprocessableEntityException("Either code or coding should be provided, can not accept both."); + throw new InvalidRequestException("Either code or coding should be provided, can not accept both."); if (coding != null) { system = new UriType(coding.getSystem()); code = new CodeType(coding.getCode()); @@ -328,27 +327,27 @@ private List filterHead(List collections) { private void validate(UriType url, UriType system, CodeType code, Coding coding) { if (!isValid(url) || !isValid(system)) - throw new UnprocessableEntityException("Both url and system must be provided."); + throw new InvalidRequestException("Both url and system must be provided."); if (!isValid(code) && coding == null) - throw new UnprocessableEntityException("Either of code or coding must be provided."); + throw new InvalidRequestException("Either of code or coding must be provided."); if (coding != null && (!isValid(coding.getSystem()) || !isValid(coding.getCode()))) - throw new UnprocessableEntityException("Both system and code of coding must be provided."); + throw new InvalidRequestException("Both system and code of coding must be provided."); } private void validate(UriType url, IntegerType offset, IntegerType count) { if (!isValid(url)) - throw new UnprocessableEntityException("Url parameter of $expand operation must be provided."); + throw new InvalidRequestException("Url parameter of $expand operation must be provided."); if (isValid(offset) && offset.getValue() < 0) - throw new UnprocessableEntityException("Offset parameter of $expand operation can not be negative."); + throw new InvalidRequestException("Offset parameter of $expand operation can not be negative."); if (isValid(count) && count.getValue() < 0) - throw new UnprocessableEntityException("Count parameter of $expand operation can not be negative."); + throw new InvalidRequestException("Count parameter of $expand operation can not be negative."); } private void validateSystemVersion(List systemVersions) { systemVersions.stream().map(m -> m.split("\\|")[0]).collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting())) .forEach((k,v) -> { if (v > 1) { - throw new UnprocessableEntityException("ValueSet $expand parameter system-version can not contain duplicate system url. Duplicate system url="+k+" found."); + throw new InvalidRequestException("ValueSet $expand parameter system-version can not contain duplicate system url. Duplicate system url="+k+" found."); } }); } } diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/AuthtokenRepository.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/AuthtokenRepository.java new file mode 100644 index 0000000..fe5f6f7 --- /dev/null +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/AuthtokenRepository.java @@ -0,0 +1,9 @@ +package org.openconceptlab.fhir.repository; + +import org.openconceptlab.fhir.model.AuthtokenToken; + +public interface AuthtokenRepository extends BaseOclRepository{ + + AuthtokenToken findByKey(String key); + +} diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/ConceptRepository.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/ConceptRepository.java index 3fe5aa2..ffdba8f 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/ConceptRepository.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/ConceptRepository.java @@ -3,6 +3,7 @@ import org.openconceptlab.fhir.model.Concept; import org.openconceptlab.fhir.model.LocalizedText; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -32,4 +33,8 @@ public interface ConceptRepository extends BaseOclRepository{ "where cs.source_id = :sourceId\n" + "group by c1.mnemonic) as val") int findConceptCountInSource(@Param("sourceId") Long sourceId); + + @Modifying(flushAutomatically = true, clearAutomatically = true) + @Query(value = "update concepts set version = :id where id = :id", nativeQuery = true) + void updateVersion(@Param("id") Long id); } diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/OrganizationRepository.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/OrganizationRepository.java index 04b2a3c..01167b5 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/OrganizationRepository.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/OrganizationRepository.java @@ -9,4 +9,7 @@ */ @Repository public interface OrganizationRepository extends BaseOclRepository{ + + Organization findByMnemonic(String mnemonic); + } diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/SourceRepository.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/SourceRepository.java index 30b8a43..7836046 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/SourceRepository.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/SourceRepository.java @@ -24,10 +24,14 @@ public interface SourceRepository extends BaseOclRepository { List findByOrganizationMnemonicAndPublicAccessIn(String org, List publicAccess); List findByUserIdUsernameAndPublicAccessIn(String username, List publicAccess); List findByCanonicalUrlAndPublicAccessIn(String canonicalUrl, List publicAccess); + List findByCanonicalUrl(String canonicalUrl); // versioned Source findFirstByMnemonicAndVersionAndOrganizationMnemonicAndPublicAccessIn(String sourceId, String version, String orgId, List publicAccess); Source findFirstByMnemonicAndVersionAndUserIdUsernameAndPublicAccessIn(String sourceId, String version, String username, List publicAccess); + Source findFirstByMnemonicAndVersionAndOrganizationMnemonic(String sourceId, String version, String orgId); + Source findFirstByMnemonicAndVersionAndUserIdUsername(String sourceId, String version, String username); + Source findFirstByCanonicalUrlAndVersionAndPublicAccessIn(String canonicalUrl, String version, List publicAccess); Source findFirstByMnemonicAndReleasedAndPublicAccessInAndOrganizationMnemonicOrderByCreatedAtDesc(String sourceId, Boolean released, List publicAccess, @@ -45,6 +49,9 @@ Source findFirstByCanonicalUrlAndReleasedAndUserIdUsernameAndPublicAccessInOrder Source findFirstByCanonicalUrlAndVersionAndOrganizationMnemonicAndPublicAccessIn(String canonicalUrl, String version, String orgId, List publicAccess); Source findFirstByCanonicalUrlAndVersionAndUserIdUsernameAndPublicAccessIn(String canonicalUrl, String version, String username, List publicAccess); + Source findFirstByCanonicalUrlAndVersionAndOrganizationMnemonic(String canonicalUrl, String version, String orgId); + Source findFirstByCanonicalUrlAndVersionAndUserIdUsername(String canonicalUrl, String version, String username); + Source findFirstByMnemonicAndReleasedAndOrganizationMnemonicAndPublicAccessInOrderByCreatedAtDesc(String sourceId, boolean released, String orgId, List publicAccess); diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserProfilesOrganizationRepository.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserProfilesOrganizationRepository.java new file mode 100644 index 0000000..5efeeef --- /dev/null +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserProfilesOrganizationRepository.java @@ -0,0 +1,13 @@ +package org.openconceptlab.fhir.repository; + +import org.hl7.fhir.r4.model.Base; +import org.openconceptlab.fhir.model.UserProfilesOrganization; + +import java.util.List; + +public interface UserProfilesOrganizationRepository extends BaseOclRepository { + + List findByOrganizationMnemonic(String org); + List findByUserprofileUsername(String username); + +} diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserRepository.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserRepository.java index f16ef91..92774a3 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserRepository.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/repository/UserRepository.java @@ -11,5 +11,7 @@ */ @Repository public interface UserRepository extends BaseOclRepository { - List findByUsernameIs(String username); + + UserProfile findByUsername(String username); + } diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirConstants.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirConstants.java index 5f1a5f1..94947ab 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirConstants.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirConstants.java @@ -1,5 +1,8 @@ package org.openconceptlab.fhir.util; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.ValueSet; + import java.util.Arrays; import java.util.List; @@ -15,7 +18,7 @@ public final class OclFhirConstants { public static final String SYSTEM = "system"; public static final String VERSION = "version"; public static final String USE = "use"; - public static final String CONCEPT_CLASS = "conceptclass"; + public static final String CONCEPTCLASS = "conceptclass"; public static final String DATATYPE = "datatype"; public static final String PURPOSE = "purpose"; public static final String COPYRIGHT = "copyright"; @@ -74,4 +77,41 @@ public final class OclFhirConstants { public static final String INACTIVE = "inactive"; public static final String SYSTEM_HL7_CONCEPT_PROP = "http://hl7.org/fhir/concept-properties"; public static final String DESC_HL7_CONCEPT_PROP = "True if the concept is not considered active."; + + public static final String ACSN_SYSTEM = "http://hl7.org/fhir/v2/0203"; + public static final String ACSN = "ACSN"; + public static final String OCL_SYSTEM = "http://fhir.openconceptlab.org"; + + public static final String CODESYSTEM = CodeSystem.class.getSimpleName(); + public static final String VALUESET = ValueSet.class.getSimpleName(); + + public static final String EN_LOCALE = "en"; + public static final String DEFINITION = "definition"; + public static final String NA = "N/A"; + public static final String EMPTY_JSON = "{}"; + public static final String FW_SLASH = "/"; + public static final String TYPE = "type"; + public static final String LOCALE = "locale"; + public static final String LOCALE_PREFERRED = "locale_preferred"; + public static final String CREATED_AT = "created_at"; + public static final String CONCEPT_ID = "concept_id"; + public static final String LOCALIZEDTEXT_ID = "localizedtext_id"; + public static final String PUBLIC_ACCESS = "public_access"; + public static final String IS_ACTIVE = "is_active"; + public static final String EXTRAS = "extras"; + public static final String URI = "uri"; + public static final String MNEMONIC = "mnemonic"; + public static final String RELEASED = "released"; + public static final String RETIRED = "retired"; + public static final String IS_LATEST_VERSION = "is_latest_version"; + public static final String FULL_NAME = "full_name"; + public static final String DEFAULT_LOCALE = "default_locale"; + public static final String CONCEPT_CLASS = "concept_class"; + public static final String COMMENT = "comment"; + public static final String CREATED_BY_ID = "created_by_id"; + public static final String UPDATED_BY_ID = "updated_by_id"; + public static final String PARENT_ID = "parent_id"; + public static final String UPDATED_AT = "updated_at"; + + public static final String AUTHORIZATION = "Authorization"; } diff --git a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirUtil.java b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirUtil.java index 3b6a80b..4f4efb7 100644 --- a/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirUtil.java +++ b/ocl-fhir-ts/src/main/java/org/openconceptlab/fhir/util/OclFhirUtil.java @@ -3,8 +3,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.*; import com.google.gson.*; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; @@ -13,9 +12,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.*; import org.openconceptlab.fhir.model.*; -import org.openconceptlab.fhir.repository.ConceptRepository; -import org.openconceptlab.fhir.repository.ConceptsSourceRepository; -import org.openconceptlab.fhir.repository.SourceRepository; +import org.openconceptlab.fhir.repository.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; @@ -57,12 +54,9 @@ public OclFhirUtil(){ } private String serverBase = ""; - IParser parser = context.newJsonParser(); + public static IParser parser = context.newJsonParser(); public static JsonParser jsonParser = new JsonParser(); public static Gson gson = new Gson(); - public static final List allowedFilterOperators = Arrays.asList(CodeSystem.FilterOperator.ISA.toCode(), - CodeSystem.FilterOperator.ISNOTA.toCode(), CodeSystem.FilterOperator.IN.toCode(), - CodeSystem.FilterOperator.NOTIN.toCode()); @PostConstruct private void init() { @@ -77,7 +71,7 @@ public static Bundle getBundle(List resource, String fhi Bundle bundle = new Bundle(); bundle.setType(Bundle.BundleType.SEARCHSET); bundle.setTotal(resource.size()); - resource.stream().forEach(r -> { + resource.forEach(r -> { Bundle.BundleEntryComponent component = new Bundle.BundleEntryComponent(); component.setResource(r); //component.setFullUrl(getCompleteUrl(fhirBase, requestPath, r.getId())); @@ -217,46 +211,6 @@ public static boolean isValid(final PrimitiveType type) { return type != null && !type.isEmpty(); } - public static JsonObject parseExtras(String extras) { - return jsonParser.parse(extras).getAsJsonObject(); - } - - public static List getIdentifiers(JsonArray jsonArray) { - List identifiers = new ArrayList<>(); - if (jsonArray != null && jsonArray.isJsonArray()) { - for(JsonElement je : jsonArray) { - JsonObject identifier = je.getAsJsonObject(); - if(identifier.get(SYSTEM) != null && identifier.get(VALUE) != null) { - Identifier i = new Identifier(); - i.setSystem(identifier.get(SYSTEM).getAsString()); - i.setValue(identifier.get(VALUE).getAsString()); - JsonElement use = identifier.get(USE); - if(use != null && Identifier.IdentifierUse.fromCode(use.getAsString()) != null) - i.setUse(Identifier.IdentifierUse.fromCode(use.getAsString())); - identifiers.add(i); - } - } - } - return identifiers; - } - - public static boolean isValidElement(JsonElement element) { - return element != null && element.getAsString() != null; - } - - public static void validatePublisher(String publisher) { - if (!isValidPublisher(publisher)) { - throw new InvalidRequestException("", getError(OperationOutcome.IssueType.INVALID, - String.format("Invalid publisher '%s' provided. Correct format is 'user:' or 'org:'", publisher))); - } - } - - public static boolean isValidPublisher(final String publisher) { - return isValid(publisher) - && publisher.matches(PUBLISHER_REGEX) - && publisher.split(SEP).length >= 2; - } - public static String getOwnerType(String owner) { return owner.split(SEP)[0]; } @@ -277,10 +231,10 @@ public static Optional getIdentifier(String value) { if (!isValid(value)) return Optional.empty(); Identifier identifier = new Identifier(); - identifier.setSystem("http://fhir.openconceptlab.org"); - identifier.setValue(value.replace("sources", "CodeSystem").replace("collections", "ValueSet")); + identifier.setSystem(OCL_SYSTEM); + identifier.setValue(value.replace("sources", CODESYSTEM).replace("collections", VALUESET)); identifier.getType().setText("Accession ID"); - identifier.getType().getCodingFirstRep().setSystem("http://hl7.org/fhir/v2/0203").setCode("ACSN").setDisplay("Accession ID"); + identifier.getType().getCodingFirstRep().setSystem(ACSN_SYSTEM).setCode(ACSN).setDisplay("Accession ID"); return isValid(value) ? Optional.of(identifier) : Optional.empty(); } @@ -298,13 +252,15 @@ public static void addConceptDesignation(Concept concept, CodeSystem.ConceptDefi }); } - public static void addStatus(MetadataResource resource, boolean active, boolean retired, boolean released) { - if(active || released) { - resource.setStatus(Enumerations.PublicationStatus.ACTIVE); - } else if(retired) { + public static void addStatus(MetadataResource resource, boolean retired, boolean released) { + if (retired) { resource.setStatus(Enumerations.PublicationStatus.RETIRED); + return; + } + if (released) { + resource.setStatus(Enumerations.PublicationStatus.ACTIVE); } else { - resource.setStatus(Enumerations.PublicationStatus.UNKNOWN); + resource.setStatus(Enumerations.PublicationStatus.DRAFT); } } @@ -368,6 +324,10 @@ public static ResponseEntity badRequest() { return ResponseEntity.badRequest().body("{\"exception\":\"Could not process the request.\"}"); } + public static ResponseEntity badRequest(String msg) { + return ResponseEntity.badRequest().body("{\"exception\":\"" + msg + "\"}"); + } + public static StringType newStringType(UriType type) { StringType stringType = new StringType(); if (type != null) stringType.setValue(type.getValue()); @@ -439,7 +399,7 @@ private static void addJsonProperty(JsonObject object, String resourceType, Stri JsonArray array = jsonArray(value); object.add(property, array); } catch (Exception e) { - log.warn(String.format("Error parsing {}.{} ", resourceType, property) + e.getMessage(), e); + log.warn(String.format("Error parsing %s.%s ", resourceType, property) + e.getMessage(), e); } } } @@ -454,4 +414,38 @@ public static JsonArray jsonArray(String value) { } return ar; } + + public static String formatExpression(String expression) { + String uri = expression.trim(); + if (!uri.startsWith(FW_SLASH)) uri = FW_SLASH + uri; + if (!uri.endsWith(FW_SLASH)) uri = uri + FW_SLASH; + return uri; + } + + public static String getAccessionIdentifier(CodeSystem codeSystem) { + if (codeSystem != null) { + Optional hasIdentifier = hasAccessionIdentifier(codeSystem.getIdentifier()); + if (hasIdentifier.isPresent()) + return hasIdentifier.get().getValue().trim(); + } + return EMPTY; + } + + public static Optional hasAccessionIdentifier(List identifiers) { + for (Identifier identifier : identifiers) { + Optional coding = identifier.getType().getCoding().stream() + .filter(t -> ACSN_SYSTEM.equals(t.getSystem())) + .filter(t -> ACSN.equals(t.getCode())) + .findAny(); + if (coding.isPresent()) { + if (isValid(identifier.getSystem()) + && OCL_SYSTEM.equals(identifier.getSystem()) + && isValid(identifier.getValue())) { + return Optional.of(identifier); + } + } + } + return Optional.empty(); + } + } diff --git a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/base/OclFhirTest.java b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/base/OclFhirTest.java index ea745e5..bcca56a 100644 --- a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/base/OclFhirTest.java +++ b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/base/OclFhirTest.java @@ -2,7 +2,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider; import org.hl7.fhir.r4.model.*; import org.junit.Assert; import org.mockito.Mock; @@ -10,24 +9,22 @@ import org.openconceptlab.fhir.converter.CodeSystemConverter; import org.openconceptlab.fhir.converter.ValueSetConverter; import org.openconceptlab.fhir.model.*; +import org.openconceptlab.fhir.model.Collection; import org.openconceptlab.fhir.model.Organization; import org.openconceptlab.fhir.provider.CodeSystemResourceProvider; import org.openconceptlab.fhir.provider.OclCapabilityStatementProvider; import org.openconceptlab.fhir.provider.ValueSetResourceProvider; -import org.openconceptlab.fhir.repository.CollectionRepository; -import org.openconceptlab.fhir.repository.ConceptRepository; -import org.openconceptlab.fhir.repository.ConceptsSourceRepository; -import org.openconceptlab.fhir.repository.SourceRepository; +import org.openconceptlab.fhir.repository.*; import org.openconceptlab.fhir.util.OclFhirUtil; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import javax.servlet.http.HttpServletRequest; +import javax.sql.DataSource; import java.sql.Timestamp; import java.time.LocalDate; import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; +import java.util.*; import static org.mockito.Mockito.spy; import static org.openconceptlab.fhir.util.OclFhirConstants.*; @@ -101,6 +98,9 @@ public class OclFhirTest { protected ConceptsSource cs33 = conceptsSource(concept3(), source3); protected ConceptsSource cs34 = conceptsSource(concept4(), source3); + protected Date date1 = Date.from(LocalDate.of(2020, 12, 1).atStartOfDay(ZoneId.systemDefault()).toInstant()); + protected Date date2 = Date.from(LocalDate.of(2020, 12, 2).atStartOfDay(ZoneId.systemDefault()).toInstant()); + @Mock protected SourceRepository sourceRepository; @@ -113,15 +113,48 @@ public class OclFhirTest { @Mock protected ConceptsSourceRepository conceptsSourceRepository; + @Mock + protected AuthtokenRepository authtokenRepository; + + @Mock + protected UserProfilesOrganizationRepository userProfilesOrganizationRepository; + @Mock protected UserProfile oclUser; + @Mock + protected OrganizationRepository organizationRepository; + + @Mock + protected UserRepository userRepository; + @Mock protected RequestDetails requestDetails; @Mock protected HttpServletRequest servletRequest; + @Mock + protected DataSource dataSource; + + @Mock + protected SimpleJdbcInsert insertLocalizedText; + + @Mock + protected SimpleJdbcInsert insertConceptName; + + @Mock + protected SimpleJdbcInsert insertConceptDesc; + + @Mock + protected SimpleJdbcInsert insertSource; + + @Mock + protected SimpleJdbcInsert insertConcept; + + @Mock + protected JdbcTemplate jdbcTemplate; + @Spy protected OclCapabilityStatementProvider capabilityStatementProvider; @@ -165,16 +198,38 @@ public boolean isValid(String value) { public ValueSetResourceProvider valueSetProvider() { OclFhirUtil oclFhirUtil = new OclFhirUtil(sourceRepository, conceptRepository, conceptsSourceRepository); - ValueSetConverter converter = new ValueSetConverter(oclFhirUtil, conceptsSourceRepository, conceptRepository, sourceRepository); - ValueSetResourceProvider provider = spy(new ValueSetResourceProvider(collectionRepository, converter, oclFhirUtil)); - return provider; + ValueSetConverter converter = new ValueSetConverter(sourceRepository, conceptRepository, oclFhirUtil, oclUser, conceptsSourceRepository, dataSource, + authtokenRepository, userProfilesOrganizationRepository, organizationRepository, userRepository); + return spy(new ValueSetResourceProvider(collectionRepository, converter, oclFhirUtil)); + } + + class TestCodeSystemConverter extends CodeSystemConverter { + + public TestCodeSystemConverter(SourceRepository sourceRepository, ConceptRepository conceptRepository, OclFhirUtil oclFhirUtil, + UserProfile oclUser, ConceptsSourceRepository conceptsSourceRepository, DataSource dataSource, + AuthtokenRepository authtokenRepository, UserProfilesOrganizationRepository userProfilesOrganizationRepository, + OrganizationRepository organizationRepository, UserRepository userRepository) { + super(sourceRepository, conceptRepository, oclFhirUtil, oclUser, conceptsSourceRepository, dataSource, authtokenRepository, + userProfilesOrganizationRepository, organizationRepository, userRepository); + this.jdbcTemplate = OclFhirTest.this.jdbcTemplate; + this.insertConcept = OclFhirTest.this.insertConcept; + this.insertLocalizedText = OclFhirTest.this.insertLocalizedText; + } + + @Override + public void init() { + this.jdbcTemplate = OclFhirTest.this.jdbcTemplate; + this.insertConcept = OclFhirTest.this.insertConcept; + this.insertLocalizedText = OclFhirTest.this.insertLocalizedText; + } } public CodeSystemResourceProvider codeSystemProvider() { OclFhirUtil oclFhirUtil = new OclFhirUtil(sourceRepository, conceptRepository, conceptsSourceRepository); - CodeSystemConverter converter = new CodeSystemConverter(sourceRepository, conceptRepository, oclFhirUtil, oclUser, conceptsSourceRepository); - CodeSystemResourceProvider provider = new CodeSystemResourceProvider(sourceRepository, converter, oclFhirUtil); - return provider; + CodeSystemConverter converter = new TestCodeSystemConverter(sourceRepository, conceptRepository, oclFhirUtil, + oclUser, conceptsSourceRepository, dataSource, authtokenRepository, userProfilesOrganizationRepository, + organizationRepository, userRepository); + return new CodeSystemResourceProvider(sourceRepository, converter, oclFhirUtil); } public UriType newUrl(String url) { @@ -224,7 +279,7 @@ public Source source(Long id, String version, Concept... concepts) { Source source = new Source(); source.setId(id); source.setCanonicalUrl(CS_URL); - source.setMnemonic("diagnosis-cs"); + source.setMnemonic(CS); source.setVersion(version); for (Concept concept : concepts) { source.getConcepts().add(concept); @@ -242,26 +297,6 @@ public Concept concept(Long id, String code) { return concept; } - public void setActive(Concept concept) { - concept.setIsActive(true); - } - - public void setReleased(Source source) { - source.setReleased(true); - } - - public void setRetired(Source source) { - source.setRetired(true); - } - - public void setReleased(Collection collection) { - collection.setReleased(true); - } - - public void setRetired(Collection collection) { - collection.setRetired(true); - } - public ConceptsName newName( String name, String type, String locale, Boolean preferred) { ConceptsName cname = new ConceptsName(); cname.setLocalizedText(newText(name, type, locale, preferred)); @@ -329,13 +364,14 @@ protected void populateSource1(Source source1) { source1.setFullName(SOURCE_1_FULL_NAME); source1.setIsActive(true); source1.setDescription(SOURCE_1_DESCRIPTION); - source1.setContact("[{\"name\": \"Jon Doe 1\", \"telecom\": [{\"use\": \"work\", \"rank\": 1, \"value\": \"jondoe1@gmail.com\", \"period\": {\"end\": \"2025-10-29T10:26:15-04:00\", \"start\": \"2020-10-29T10:26:15-04:00\"}, \"system\": \"email\"}]}]"); - source1.setJurisdiction("[{\"coding\": [{\"code\": \"USA\", \"system\": \"http://unstats.un.org/unsd/methods/m49/m49.htm\", \"display\": \"United States of America\"}]}]"); + source1.setContact("[{\"name\": \"Jon Doe 1\", \"telecom\": [{\"use\": \"work\", \"rank\": 1, \"value\": \"jondoe1@gmail.com\", " + + "\"period\": {\"end\": \"2025-10-29T10:26:15-04:00\", \"start\": \"2020-10-29T10:26:15-04:00\"}, \"system\": \"email\"}]}]"); + source1.setJurisdiction("[{\"coding\": [{\"code\": \"USA\", \"system\": \"http://unstats.un.org/unsd/methods/m49/m49.htm\", " + + "\"display\": \"United States of America\"}]}]"); source1.setPublisher(TEST); source1.setPurpose(TEST_SOURCE); source1.setCopyright(SOURCE_1_COPYRIGHT_TEXT); source1.setContentType(EXAMPLE); - Date date1 = Date.from(LocalDate.of(2020, 12, 1).atStartOfDay(ZoneId.systemDefault()).toInstant()); source1.setRevisionDate(date1); source1.setCreatedAt(new Timestamp(new Date().getTime())); } @@ -348,15 +384,48 @@ protected void populateSource2(Source source2) { source2.setFullName(SOURCE_2_FULL_NAME); source2.setIsActive(true); source2.setDescription(SOURCE_2_DESCRIPTION); - source2.setContact("[{\"name\": \"Jon Doe 2\", \"telecom\": [{\"use\": \"work\", \"rank\": 1, \"value\": \"jondoe2@gmail.com\", \"period\": {\"end\": \"2022-10-29T10:26:15-04:00\", \"start\": \"2021-10-29T10:26:15-04:00\"}, \"system\": \"email\"}]}]"); - source2.setJurisdiction("[{\"coding\": [{\"code\": \"ETH\", \"system\": \"http://unstats.un.org/unsd/methods/m49/m49.htm\", \"display\": \"Ethiopia\"}]}]"); + source2.setContact("[{\"name\": \"Jon Doe 2\", \"telecom\": [{\"use\": \"work\", \"rank\": 1, \"value\": \"jondoe2@gmail.com\", " + + "\"period\": {\"end\": \"2022-10-29T10:26:15-04:00\", \"start\": \"2021-10-29T10:26:15-04:00\"}, \"system\": \"email\"}]}]"); + source2.setJurisdiction("[{\"coding\": [{\"code\": \"ETH\", \"system\": \"http://unstats.un.org/unsd/methods/m49/m49.htm\", " + + "\"display\": \"Ethiopia\"}]}]"); source2.setPublisher("TEST"); source2.setPurpose(TEST_SOURCE); source2.setCopyright(SOURCE_2_COPYRIGHT_TEXT); source2.setContentType(EXAMPLE); - Date date2 = Date.from(LocalDate.of(2020, 12, 2).atStartOfDay(ZoneId.systemDefault()).toInstant()); source2.setRevisionDate(date2); source1.setCreatedAt(new Timestamp(new Date().getTime())); } + protected Organization newOrganization() { + Organization organization = new Organization(); + organization.setMnemonic("OCL"); + organization.setId(789L); + return organization; + } + + protected UserProfile newUser(String username) { + UserProfile user = new UserProfile(); + user.setUsername(username); + user.setId(567L); + AuthtokenToken token = new AuthtokenToken(); + token.setUserProfile(user); + token.setKey("12345"); + user.setAuthtokenTokens(Collections.singletonList(token)); + return user; + } + + protected AuthtokenToken newToken(String username) { + AuthtokenToken token = new AuthtokenToken(); + token.setUserProfile(newUser(username)); + token.setKey("12345"); + return token; + } + + protected UserProfilesOrganization newUserOrg(String username) { + UserProfilesOrganization userOrg = new UserProfilesOrganization(); + userOrg.setUserProfile(newUser(username)); + userOrg.setOrganization(newOrganization()); + return userOrg; + } + } diff --git a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestCodeSystemResourceProvider.java b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestCodeSystemResourceProvider.java index d083e4b..d92bbbe 100644 --- a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestCodeSystemResourceProvider.java +++ b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestCodeSystemResourceProvider.java @@ -1,28 +1,38 @@ package org.openconceptlab.fhir.provider; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import com.openpojo.reflection.impl.PojoClassFactory; import com.openpojo.validation.Validator; import com.openpojo.validation.ValidatorBuilder; import com.openpojo.validation.test.impl.GetterTester; import com.openpojo.validation.test.impl.SetterTester; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.CodeSystem; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Parameters; +import org.aspectj.apache.bcel.classfile.Code; +import org.hl7.fhir.r4.model.*; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.mockito.verification.VerificationMode; import org.openconceptlab.fhir.base.OclFhirTest; import org.openconceptlab.fhir.model.*; import org.openconceptlab.fhir.model.Collection; +import org.openconceptlab.fhir.model.Organization; +import org.openconceptlab.fhir.provider.CodeSystemResourceProvider; import org.springframework.data.domain.PageRequest; +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; import static org.mockito.Mockito.*; import static org.junit.Assert.*; +import static org.openconceptlab.fhir.util.OclFhirConstants.*; import java.time.LocalDate; import java.time.ZoneId; @@ -30,6 +40,8 @@ public class TestCodeSystemResourceProvider extends OclFhirTest { + private static final String test_user = "testuser"; + @Before public void setUpBefore() { MockitoAnnotations.initMocks(this); @@ -541,6 +553,211 @@ public void testDTO() { validateAccessors(UserProfilesUserPermission.class); } + @Test(expected = InvalidRequestException.class) + public void testCreateCodeSystem_null() { + CodeSystemResourceProvider provider = codeSystemProvider(); + provider.createCodeSystem(null, requestDetails); + } + + @Test(expected = InvalidRequestException.class) + public void testCreateCodeSystem_accessionId_null() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + codeSystem.setIdentifier(null); + provider.createCodeSystem(codeSystem, requestDetails); + } + + @Test(expected = InvalidRequestException.class) + public void testCreateCodeSystem_url_null() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + codeSystem.setUrl(null); + provider.createCodeSystem(codeSystem, requestDetails); + } + + @Test(expected = InvalidRequestException.class) + public void testCreateCodeSystem_invalid_org() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + when(organizationRepository.findByMnemonic(anyString())).thenReturn(null); + provider.createCodeSystem(codeSystem, requestDetails); + verify(organizationRepository, times(1)).findByMnemonic(anyString()); + } + + @Test(expected = InvalidRequestException.class) + public void testCreateCodeSystem_invalid_user() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + codeSystem.getIdentifierFirstRep().setValue("/users/testuser/CodeSystem/testsource/2.0"); + when(userRepository.findByUsername(anyString())).thenReturn(null); + provider.createCodeSystem(codeSystem, requestDetails); + verify(userRepository, times(1)).findByUsername(anyString()); + } + + @Test(expected = AuthenticationException.class) + public void testCreateCodeSystem_token_null() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + when(requestDetails.getHeader(anyString())).thenReturn(null); + when(organizationRepository.findByMnemonic(anyString())).thenReturn(newOrganization()); + provider.createCodeSystem(codeSystem, requestDetails); + verify(requestDetails, times(1)).getHeader(anyString()); + verify(organizationRepository, times(1)).findByMnemonic(anyString()); + } + + @Test(expected = AuthenticationException.class) + public void testCreateCodeSystem_invalid_token() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + when(requestDetails.getHeader(anyString())).thenReturn("Token 678423578911230985"); + when(organizationRepository.findByMnemonic(anyString())).thenReturn(newOrganization()); + when(authtokenRepository.findByKey(anyString())).thenReturn(null); + provider.createCodeSystem(codeSystem, requestDetails); + verify(requestDetails, times(1)).getHeader(anyString()); + verify(organizationRepository, times(1)).findByMnemonic(anyString()); + verify(authtokenRepository, times(1)).findByKey(anyString()); + } + + @Test(expected = AuthenticationException.class) + public void testCreateCodeSystem_user_not_org_member_invalid_user() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + when(requestDetails.getHeader(anyString())).thenReturn("Token 12345"); + when(organizationRepository.findByMnemonic(anyString())).thenReturn(newOrganization()); + when(authtokenRepository.findByKey(anyString())).thenReturn(newToken(test_user)); + when(userProfilesOrganizationRepository.findByOrganizationMnemonic(anyString())) + .thenReturn(Collections.singletonList(newUserOrg("otheruser"))); + provider.createCodeSystem(codeSystem, requestDetails); + verify(requestDetails, times(1)).getHeader(anyString()); + verify(organizationRepository, times(1)).findByMnemonic(anyString()); + verify(authtokenRepository, times(1)).findByKey(anyString()); + verify(userProfilesOrganizationRepository, times(1)).findByOrganizationMnemonic(anyString()); + } + + @Test(expected = ResourceVersionConflictException.class) + public void testCreateCodeSystem_codesystem_id_exists() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + when(requestDetails.getHeader(anyString())).thenReturn("Token 12345"); + when(organizationRepository.findByMnemonic(anyString())).thenReturn(newOrganization()); + when(authtokenRepository.findByKey(anyString())).thenReturn(newToken(test_user)); + when(userProfilesOrganizationRepository.findByOrganizationMnemonic(anyString())) + .thenReturn(Collections.singletonList(newUserOrg(test_user))); + when(sourceRepository.findFirstByMnemonicAndVersionAndOrganizationMnemonic(anyString(), anyString(), anyString())) + .thenReturn(new Source()); + provider.createCodeSystem(codeSystem, requestDetails); + verify(requestDetails, times(1)).getHeader(anyString()); + verify(organizationRepository, times(1)).findByMnemonic(anyString()); + verify(authtokenRepository, times(1)).findByKey(anyString()); + verify(userProfilesOrganizationRepository, times(1)).findByOrganizationMnemonic(anyString()); + verify(sourceRepository, times(1)).findFirstByMnemonicAndVersionAndOrganizationMnemonic(anyString(), anyString(), anyString()); + } + + @Test(expected = ResourceVersionConflictException.class) + public void testCreateCodeSystem_codesystem_url_exists() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + when(requestDetails.getHeader(anyString())).thenReturn("Token 12345"); + when(organizationRepository.findByMnemonic(anyString())).thenReturn(newOrganization()); + when(authtokenRepository.findByKey(anyString())).thenReturn(newToken(test_user)); + when(userProfilesOrganizationRepository.findByOrganizationMnemonic(anyString())) + .thenReturn(Collections.singletonList(newUserOrg(test_user))); + when(sourceRepository.findFirstByCanonicalUrlAndVersionAndOrganizationMnemonic(anyString(), anyString(), anyString())) + .thenReturn(new Source()); + provider.createCodeSystem(codeSystem, requestDetails); + verify(requestDetails, times(1)).getHeader(anyString()); + verify(organizationRepository, times(1)).findByMnemonic(anyString()); + verify(authtokenRepository, times(1)).findByKey(anyString()); + verify(userProfilesOrganizationRepository, times(1)).findByOrganizationMnemonic(anyString()); + verify(sourceRepository, times(1)).findFirstByCanonicalUrlAndVersionAndOrganizationMnemonic(anyString(), anyString(), anyString()); + } + + @Test + public void testCreateCodeSystem() { + CodeSystemResourceProvider provider = codeSystemProvider(); + CodeSystem codeSystem = codeSystem(); + when(requestDetails.getHeader(anyString())).thenReturn("Token 12345"); + when(organizationRepository.findByMnemonic(anyString())).thenReturn(newOrganization()); + when(authtokenRepository.findByKey(anyString())).thenReturn(newToken(test_user)); + when(userProfilesOrganizationRepository.findByOrganizationMnemonic(anyString())) + .thenReturn(Collections.singletonList(newUserOrg(test_user))); + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + ((Source)args[0]).setId(123L); + return args[0]; + } + }).when(sourceRepository).saveAndFlush(any(Source.class)); + + when(insertConcept.executeAndReturnKeyHolder(anyMap())).thenReturn(newKey()); + when(insertLocalizedText.executeAndReturnKeyHolder(anyMap())).thenReturn(newKey(), newKey()); + + doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return null; + } + }).when(jdbcTemplate).batchUpdate(anyString(), any(BatchPreparedStatementSetter.class)); + + provider.createCodeSystem(codeSystem, requestDetails); + verify(requestDetails, times(1)).getHeader(anyString()); + verify(organizationRepository, times(1)).findByMnemonic(anyString()); + verify(authtokenRepository, times(1)).findByKey(anyString()); + verify(userProfilesOrganizationRepository, times(1)).findByOrganizationMnemonic(anyString()); + verify(sourceRepository, times(1)).saveAndFlush(any(Source.class)); + verify(insertConcept, times(1)).executeAndReturnKeyHolder(anyMap()); + verify(insertLocalizedText, times(2)).executeAndReturnKeyHolder(anyMap()); + } + + private KeyHolder newKey() { + List> list = new ArrayList<>(); + Map map = new HashMap<>(); + map.put("id", 45); + list.add(map); + return new GeneratedKeyHolder(list); + } + + private CodeSystem codeSystem() { + CodeSystem system = new CodeSystem(); + system.setUrl(URL_SOURCE_1); + Identifier identifier = system.getIdentifierFirstRep(); + identifier.getType().getCodingFirstRep().setSystem(ACSN_SYSTEM).setCode(ACSN); + identifier.setSystem(OCL_SYSTEM); + identifier.setValue("/orgs/OCL/CodeSystem/testsource/2.0"); + system.setName("Test Code System"); + system.setStatus(Enumerations.PublicationStatus.DRAFT); + system.setContent(CodeSystem.CodeSystemContentMode.EXAMPLE); + system.setCopyright("Test copy right"); + system.setPurpose("Test purpose"); + system.setPublisher("Test publisher"); + system.setDescription("Test description"); + system.setTitle("Test title"); + system.setDate(date1); + + system.getContactFirstRep().setName("Jon Doe").getTelecomFirstRep().setSystem(ContactPoint.ContactPointSystem.EMAIL) + .setValue("jondoe@gmail.com").setUse(ContactPoint.ContactPointUse.WORK).setRank(1); + + system.getJurisdictionFirstRep().getCodingFirstRep().setSystem("http://unstats.un.org/unsd/methods/m49/m49.htm") + .setCode("USA").setDisplay("United States of America"); + + CodeSystem.ConceptDefinitionComponent component = system.getConceptFirstRep(); + component.setCode("Concept1"); + component.setDisplay("concept display"); + component.setDefinition("concept definition"); + Coding coding = new Coding(); + coding.setCode("Synonym"); + component.getDesignationFirstRep().setLanguage("en").setUse(coding).setValue("designation display"); + CodeSystem.ConceptPropertyComponent c1 = new CodeSystem.ConceptPropertyComponent(); + c1.setCode(CONCEPT_CLASS).setValue(newString("test_class")); + CodeSystem.ConceptPropertyComponent c2 = new CodeSystem.ConceptPropertyComponent(); + c2.setCode(DATATYPE).setValue(newString("test_data_type")); + CodeSystem.ConceptPropertyComponent c3 = new CodeSystem.ConceptPropertyComponent(); + c1.setCode(INACTIVE).setValue(new BooleanType(false)); + component.getProperty().addAll(Arrays.asList(c1,c2,c3)); + return system; + } + private void assertConcept(CodeSystem.ConceptDefinitionComponent c, String code, String display, String language1, String name1, String language2, String name2, String prop1, String val1, String prop2, String val2, String prop3, String val3) { diff --git a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestOclCapabilityStatementProvider.java b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestOclCapabilityStatementProvider.java index 20986a6..22d4082 100644 --- a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestOclCapabilityStatementProvider.java +++ b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestOclCapabilityStatementProvider.java @@ -13,6 +13,8 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.openconceptlab.fhir.base.OclFhirTest; +import org.openconceptlab.fhir.provider.OclCapabilityStatementProvider; + import static org.junit.Assert.*; import static org.mockito.Mockito.*; import static org.openconceptlab.fhir.provider.OclCapabilityStatementProvider.*; diff --git a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestValueSetResourceProvider.java b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestValueSetResourceProvider.java index 04db9e2..78a4a98 100644 --- a/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestValueSetResourceProvider.java +++ b/ocl-fhir-ts/src/test/java/org/openconceptlab/fhir/provider/TestValueSetResourceProvider.java @@ -2,8 +2,8 @@ import static org.mockito.Mockito.*; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.*; @@ -16,6 +16,7 @@ import org.mockito.stubbing.OngoingStubbing; import org.openconceptlab.fhir.base.OclFhirTest; import org.openconceptlab.fhir.model.*; +import org.openconceptlab.fhir.provider.ValueSetResourceProvider; import org.springframework.beans.factory.annotation.Value; import java.time.LocalDate; @@ -416,22 +417,22 @@ private void assertBaseValueSet(ValueSet valueSet, String url, String name, Stri assertEquals(copyright, valueSet.getCopyright()); } - @Test(expected = UnprocessableEntityException.class) + @Test(expected = InvalidRequestException.class) public void testValidateCode_url_null() { validateCode(null, V_11_1, CS_URL, V_21_1, AD, null, null, null, OWNER_VAL); } - @Test(expected = UnprocessableEntityException.class) + @Test(expected = InvalidRequestException.class) public void testValidateCode_code_null() { validateCode(VS_URL, V_11_1, CS_URL, V_21_1, null, null, null, null, OWNER_VAL); } - @Test(expected = UnprocessableEntityException.class) + @Test(expected = InvalidRequestException.class) public void testValidateCode_system_null() { validateCode(VS_URL, V_11_1, null, V_21_1, AD, null, null, null, OWNER_VAL); } - @Test(expected = UnprocessableEntityException.class) + @Test(expected = InvalidRequestException.class) public void testValidateCode_code_coding() { validateCode(VS_URL, V_11_1, CS_URL, null, AD, null, null, new Coding("ABC", "ABC", "ABC"), OWNER_VAL); @@ -539,7 +540,7 @@ public void testExpand_count_0() { assertEquals(0, vs.getExpansion().getContains().size()); } - @Test(expected = UnprocessableEntityException.class) + @Test(expected = InvalidRequestException.class) public void testExpand_count_negative() { List references = newReferences( "/orgs/OCL/sources/"+CS+"/v1.0/concepts/"+AD+"/123/", @@ -551,14 +552,14 @@ public void testExpand_count_negative() { runExpand(references, Collections.singletonList(cs11), Collections.singletonList(cs21), null, 0, -2, ""); } - @Test(expected = UnprocessableEntityException.class) + @Test(expected = InvalidRequestException.class) public void testExpand_missing_url() { ValueSetResourceProvider provider = valueSetProvider(); provider.valueSetExpand(null, null, new IntegerType(0), new IntegerType(10), null, null, null, null, null, null, null, newString(OWNER_VAL)); } - @Test(expected = UnprocessableEntityException.class) + @Test(expected = InvalidRequestException.class) public void testExpand_unknown_systemversion() { // all match List references = newReferences(