Skip to content

Commit

Permalink
OpenConceptLab/ocl_issues#274 CodeSystem Search by owner and ownertype
Browse files Browse the repository at this point in the history
  • Loading branch information
Harsh Patel committed Sep 30, 2020
1 parent b52a23f commit c998afd
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 55 deletions.
Expand Up @@ -12,12 +12,11 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.apache.bcel.classfile.Code;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.OperationOutcome;
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.OclFhirUtil.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -48,17 +47,17 @@ public OclFhirController(CodeSystemResourceProvider codeSystemResourceProvider,

@GetMapping(path = {"/orgs/{org}/CodeSystem/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE})
public String getCodeSystemByOrg(@PathVariable String org, @PathVariable String id) {
return getCodeSystemByOwner(org, id);
return getResourceByOwner(CodeSystem.class, ORG_ + org, id);
}

@GetMapping(path = {"/users/{user}/CodeSystem/{id}"}, produces = {MediaType.APPLICATION_JSON_VALUE})
public String getCodeSystemByUser(@PathVariable String user, @PathVariable String id) {
return getCodeSystemByOwner(user, id);
return getResourceByOwner(CodeSystem.class, USER_ + user, id);
}

@GetMapping(path = {"/orgs/{org}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE})
public String searchCodeSystemsByOrg(@PathVariable String org) {
return searchCodeSystem(CodeSystem.SP_PUBLISHER, org);
return searchResource(CodeSystem.class, CodeSystem.SP_PUBLISHER, ORG_ + org);
}

@GetMapping(path = {"/users/{user}/CodeSystem"}, produces = {MediaType.APPLICATION_JSON_VALUE})
Expand Down Expand Up @@ -93,5 +92,32 @@ private String getCodeSystemByOwner(final String owner, final String id) {
return codeSystem != null && owner.equals(codeSystem.getPublisher()) ? oclFhirUtil.getResource(codeSystem) : "{}";
}

private String searchResource(final Class<? extends MetadataResource> 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.getResource(bundle);
}

private String getResourceByOwner(final Class<? extends MetadataResource> resourceClass, final String owner, final String id) {
MetadataResource resource = null;
try {
resource = oclFhirUtil.getClient()
.read().resource(resourceClass)
.withId(id).execute();
} catch (Exception e) {
return oclFhirUtil.getResource(oclFhirUtil.getNotFoundOutcome(new IdType(resourceClass.getSimpleName(), id)));
}

return resource != null && owner.equals(resource.getPublisher()) ? oclFhirUtil.getResource(resource) : "{}";
}

}
@@ -1,6 +1,5 @@
package org.openconceptlab.fhir.converter;

import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
Expand All @@ -11,8 +10,6 @@
import org.hl7.fhir.r4.model.CodeSystem.FilterOperator;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Identifier.IdentifierUse;
import org.hl7.fhir.r4.model.StringType;
import org.openconceptlab.fhir.model.Concept;
import org.openconceptlab.fhir.model.ConceptsName;
Expand All @@ -21,6 +18,8 @@
import org.openconceptlab.fhir.model.Source;
import org.openconceptlab.fhir.model.UserProfile;
import static org.openconceptlab.fhir.util.OclFhirConstants.*;
import static org.openconceptlab.fhir.util.OclFhirUtil.*;

import org.springframework.stereotype.Component;

import com.google.gson.JsonArray;
Expand All @@ -34,9 +33,7 @@
*/
@Component
public class CodeSystemConverter {

private static final List<String> allowedFilterOperators = Arrays.asList(FilterOperator.ISA.toString(), FilterOperator.ISNOTA.toString(),
FilterOperator.IN.toString(), FilterOperator.NOTIN.toString());

JsonParser parser = new JsonParser();

public void convertToCodeSystem(List<CodeSystem> codeSystems, List<Source> sources, boolean includeConcepts, String version) {
Expand Down Expand Up @@ -73,9 +70,9 @@ public CodeSystem toBaseCodeSystem(final Source source){
Organization organization = source.getOrganization();
UserProfile user = source.getUserId();
if(organization != null) {
codeSystem.setPublisher(organization.getMnemonic());
codeSystem.setPublisher(ORG_ + organization.getMnemonic());
} else if(user != null) {
codeSystem.setPublisher(user.getUsername());
codeSystem.setPublisher(USER_ + user.getUsername());
}
// set description
if(StringUtils.isNotBlank(source.getDescription())) {
Expand All @@ -88,7 +85,8 @@ public CodeSystem toBaseCodeSystem(final Source source){
codeSystem.setUrl(source.getExternalId());
}
// set status
addStatus(codeSystem, source.getIsActive(), source.getRetired() != null ? source.getRetired() : false, source.getReleased());
addStatus(codeSystem, source.getIsActive(), source.getRetired() != null ? source.getRetired() : false,
source.getReleased());
return codeSystem;
}

Expand Down Expand Up @@ -143,44 +141,28 @@ private void addExtras(CodeSystem codeSystem, String extras) {
if(filter.get(CODE) != null) {
String code = filter.get(CODE).getAsString();
if(FILTER_CODE_CC.equals(code)) {
addFilter(codeSystem, FILTER_CODE_CC, filter.get(DESC), filter.get(OP),
addFilter(codeSystem, FILTER_CODE_CC, filter.get(DESC), filter.get(OPERATOR),
filter.get(VALUE));
} else if (FILTER_CODE_DT.equals(code)) {
addFilter(codeSystem, FILTER_CODE_DT, filter.get(DESC), filter.get(OP),
addFilter(codeSystem, FILTER_CODE_DT, filter.get(DESC), filter.get(OPERATOR),
filter.get(VALUE));
}
}
}
}
// identifier
JsonArray identifiers = obj.getAsJsonArray(IDENTIFIERS);
if (identifiers != null && identifiers.isJsonArray()) {
for(JsonElement je : identifiers) {
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 && IdentifierUse.fromCode(use.getAsString()) != null)
i.setUse(IdentifierUse.fromCode(use.getAsString()));
codeSystem.getIdentifier().add(i);
}
}
}
codeSystem.setIdentifier(getIdentifiers(identifiers));

// purpose
if(isValidParam(obj.get(PURPOSE)))
if(isValidElement(obj.get(PURPOSE)))
codeSystem.setPurpose(obj.get(PURPOSE).getAsString());
// copyright
if(isValidParam(obj.get(COPYRIGHT)))
if(isValidElement(obj.get(COPYRIGHT)))
codeSystem.setCopyright(obj.get(COPYRIGHT).getAsString());
}
}

private boolean isValidParam(JsonElement element) {
return element != null && element.getAsString() != null;
}

private void addFilter(CodeSystem codeSystem, String code, JsonElement description, JsonElement operator,
JsonElement value) {
if(StringUtils.isNotBlank(code)) {
Expand All @@ -198,10 +180,6 @@ private void addFilter(CodeSystem codeSystem, String code, JsonElement descripti
codeSystem.getFilter().add(c);
}
}

private JsonObject parseExtras(String extras) {
return parser.parse(extras).getAsJsonObject();
}

public void toSource(final CodeSystem codeSystem){

Expand Down
Expand Up @@ -3,6 +3,7 @@
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 org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
Expand All @@ -11,6 +12,7 @@
import org.openconceptlab.fhir.model.*;
import org.openconceptlab.fhir.repository.*;
import org.openconceptlab.fhir.util.OclFhirUtil;
import static org.openconceptlab.fhir.util.OclFhirUtil.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand Down Expand Up @@ -40,22 +42,20 @@ public Class<? extends IBaseResource> getResourceType() {

/**
* Get a single {@link CodeSystem} for given {@link IdType}.
* @param id
* @return
* @param id {@link CodeSystem#id}
* @return {@link CodeSystem}
*/
@Read()
@Transactional
public CodeSystem get(@IdParam IdType id){
List<CodeSystem> codeSystems = new ArrayList<>();
List<Source> sources = getPublicSourceByMnemonic(new StringType(id.getIdPart()));
codeSystemConverter.convertToCodeSystem(codeSystems, sources, true, null);
if(codeSystems.size() >= 1) return codeSystems.get(0);

if(codeSystems.isEmpty()) {
if(codeSystems.size() >= 1) {
return codeSystems.get(0);
} else {
throw new ResourceNotFoundException(id, oclFhirUtil.getNotFoundOutcome(id));
}

return new CodeSystem();
}

/**
Expand All @@ -79,23 +79,30 @@ public Bundle getCodeSystems(RequestDetails details) {
*/
@Search
@Transactional
public Bundle getCodeSystemsByPublisher(@RequiredParam(name = CodeSystem.SP_PUBLISHER) StringType publisher, RequestDetails details) {
public Bundle getCodeSystemsByPublisher(@RequiredParam(name = CodeSystem.SP_PUBLISHER) StringType publisher,
RequestDetails details) {
validatePublisher(publisher.getValue());
List<CodeSystem> codeSystems = new ArrayList<CodeSystem>();
List<Source> sources = getPublicSourcesByPublisher(publisher.getValue());
codeSystemConverter.convertToCodeSystem(codeSystems, sources, true, null);
return OclFhirUtil.getBundle(codeSystems, details.getFhirServerBase(), details.getRequestPath());
}

private List<Source> getPublicSourceByMnemonic(StringType id) {
return sourceRepository.findByMnemonicAndPublicAccessIn(id.getValue(), Arrays.asList("View", "Edit"));
return sourceRepository.findByMnemonicAndPublicAccessIn(id.getValue(), publicAccess);
}

private List<Source> getPublicSources() {
return sourceRepository.findByPublicAccessIn(Arrays.asList("View", "Edit"));
return sourceRepository.findByPublicAccessIn(publicAccess);
}

private List<Source> getPublicSourcesByPublisher(String publisher) {
return sourceRepository.findByOrganizationMnemonicOrUserIdUsername(publisher, publisher);
String owner = getOwner(publisher);
String value = getPublisher(publisher);
if(ORG.equals(owner)) {
return sourceRepository.findByOrganizationMnemonic(value);
} else {
return sourceRepository.findByUserIdUsername(value);
}
}

}
Expand Up @@ -4,7 +4,14 @@
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
Expand All @@ -13,17 +20,33 @@

import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.openconceptlab.fhir.util.OclFhirConstants.*;

@Component
public class OclFhirUtil {

public static final String PUBLISHER_REGEX = "^user:.*|^org:.*";
public static final String ORG_ = "org:";
public static final String USER_ = "user:";
public static final String ORG = "org";
public static final String USER = "user";
public static final String SEP = ":";
public static List<String> publicAccess = Arrays.asList("View", "Edit");

@Value("${server.port}")
private String port;

private static FhirContext context = FhirContext.forR4();
private String serverBase = "";
IParser parser = context.newJsonParser();
public static JsonParser gson = new JsonParser();
public static final List<String> allowedFilterOperators = Arrays.asList(CodeSystem.FilterOperator.ISA.toCode(),
CodeSystem.FilterOperator.ISNOTA.toCode(), CodeSystem.FilterOperator.IN.toCode(),
CodeSystem.FilterOperator.NOTIN.toCode());

@PostConstruct
private void init() {
Expand Down Expand Up @@ -67,4 +90,78 @@ public OperationOutcome getNotFoundOutcome(IdType id) {
.setDiagnostics("Resource " + id + " does not exist.");
return o;
}

public static OperationOutcome getError(OperationOutcome.IssueType errorType, final String error) {
OperationOutcome o = new OperationOutcome();
o.getIssueFirstRep()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setCode(errorType)
.setDiagnostics(error);
return o;
}

public static OperationOutcome getError(OperationOutcome.IssueType errorType) {
OperationOutcome o = new OperationOutcome();
o.getIssueFirstRep()
.setSeverity(OperationOutcome.IssueSeverity.ERROR)
.setCode(errorType);
return o;
}

public static boolean isValid(final String value) {
return StringUtils.isNotBlank(value);
}

public static boolean isValid(final StringType value) {
return value != null && StringUtils.isNotBlank(value.getValue());
}

public static JsonObject parseExtras(String extras) {
return gson.parse(extras).getAsJsonObject();
}

public static List<Identifier> getIdentifiers(JsonArray jsonArray) {
List<Identifier> 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:<username>' or 'org:<organizationId>'", publisher)));
}
}

public static boolean isValidPublisher(final String publisher) {
return isValid(publisher)
&& publisher.matches(PUBLISHER_REGEX)
&& publisher.split(SEP).length >= 2;
}

public static String getOwner(String publisher) {
return publisher.split(SEP)[0];
}

public static String getPublisher(String publisher) {
String[] arr = publisher.split(SEP);
return String.join(SEP, ArrayUtils.subarray(arr, 1, arr.length));
}
}

0 comments on commit c998afd

Please sign in to comment.