package ca.uhn.fhir.to; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.TreeSet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.ui.ModelMap; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import com.google.gson.stream.JsonWriter; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu.resource.Conformance.RestQuery; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam; import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum; import ca.uhn.fhir.model.primitive.BoundCodeDt; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.client.GenericClient; import ca.uhn.fhir.rest.gclient.*; import ca.uhn.fhir.rest.gclient.NumberClientParam.IMatches; import ca.uhn.fhir.rest.gclient.QuantityClientParam.IAndUnits; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.to.model.HomeRequest; import ca.uhn.fhir.to.model.ResourceRequest; import ca.uhn.fhir.to.model.TransactionRequest; import ca.uhn.fhir.util.ExtensionConstants; @org.springframework.stereotype.Controller() public class Controller extends BaseController { static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Controller.class); @RequestMapping(value = { "/about" }) public String actionAbout(HttpServletRequest theServletRequest, final HomeRequest theRequest, final ModelMap theModel) { addCommonParams(theServletRequest, theRequest, theModel); theModel.put("notHome", true); theModel.put("extraBreadcrumb", "About"); ourLog.info(logPrefix(theModel) + "Displayed about page"); return "about"; } @RequestMapping(value = { "/conformance" }) public String actionConformance(HttpServletRequest theServletRequest, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { addCommonParams(theServletRequest, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); ResultType returnsResource = ResultType.RESOURCE; long start = System.currentTimeMillis(); try { Class<? extends IBaseConformance> type; switch (getContext(theRequest).getVersion().getVersion()) { default: case DSTU1: type = Conformance.class; break; case DSTU2: type = ca.uhn.fhir.model.dstu2.resource.Conformance.class; break; case DSTU3: type = org.hl7.fhir.dstu3.model.CapabilityStatement.class; break; } client.fetchConformance().ofType(type).execute(); } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, "Loaded conformance", interceptor, theRequest); ourLog.info(logPrefix(theModel) + "Displayed conformance profile"); return "result"; } @RequestMapping(value = { "/create" }) public String actionCreate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "create"); return "result"; } @RequestMapping(value = { "/delete" }) public String actionDelete(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) { addCommonParams(theReq, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor); RuntimeResourceDefinition def; try { def = getResourceType(theRequest, theReq); } catch (ServletException e) { theModel.put("errorMsg", e.toString()); return "resource"; } String id = StringUtils.defaultString(theReq.getParameter("resource-delete-id")); if (StringUtils.isBlank(id)) { theModel.put("errorMsg", "No ID specified"); return "resource"; } ResultType returnsResource = ResultType.BUNDLE; String outcomeDescription = "Delete Resource"; long start = System.currentTimeMillis(); try { client.delete((Class<? extends IBaseResource>) def.getImplementingClass(), new IdDt(id)); } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest); ourLog.info(logPrefix(theModel) + "Deleted resource of type " + def.getName()); return "result"; } @RequestMapping(value = { "/get-tags" }) public String actionGetTags(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) { addCommonParams(theReq, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor); Class<? extends IBaseResource> resType = null; ResultType returnsResource = ResultType.TAGLIST; String outcomeDescription = "Tag List"; long start = System.currentTimeMillis(); try { if (isNotBlank(theReq.getParameter(PARAM_RESOURCE))) { RuntimeResourceDefinition def; try { def = getResourceType(theRequest, theReq); } catch (ServletException e) { theModel.put("errorMsg", e.toString()); return "resource"; } resType = (Class<? extends IBaseResource>) def.getImplementingClass(); String id = theReq.getParameter("resource-tags-id"); if (isNotBlank(id)) { String vid = theReq.getParameter("resource-tags-vid"); if (isNotBlank(vid)) { client.getTags().forResource(resType, id, vid).execute(); ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName() + " ID " + id + " version" + vid); } else { client.getTags().forResource(resType, id).execute(); ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName() + " ID " + id); } } else { client.getTags().forResource(resType).execute(); ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName()); } } else { client.getTags().execute(); ourLog.info(logPrefix(theModel) + "Got tags for server"); } } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest); return "result"; } @RequestMapping(value = { "/history-server" }) public String actionHistoryServer(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { doActionHistory(theReq, theRequest, theBindingResult, theModel, "history-server", "Server History"); return "result"; } @RequestMapping(value = { "/history-type" }) public String actionHistoryType(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { doActionHistory(theReq, theRequest, theBindingResult, theModel, "history-type", "History"); return "result"; } @RequestMapping(value = { "/", "/home" }) public String actionHome(HttpServletRequest theServletRequest, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { addCommonParams(theServletRequest, theRequest, theModel); ourLog.info(theServletRequest.toString()); return "home"; } @RequestMapping(value = { "/page" }) public String actionPage(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) { addCommonParams(theReq, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); FhirContext context = getContext(theRequest); GenericClient client = theRequest.newClient(theReq, context, myConfig, interceptor); String url = defaultString(theReq.getParameter("page-url")); if (!url.startsWith(theModel.get("base").toString())) { ourLog.warn(logPrefix(theModel) + "Refusing to load page URL: {}", url); theModel.put("errorMsg", "Invalid page URL: " + url); return "result"; } url = url.replace("&", "&"); ResultType returnsResource = ResultType.BUNDLE; long start = System.currentTimeMillis(); try { ourLog.info(logPrefix(theModel) + "Loading paging URL: {}", url); if (context.getVersion().getVersion() == FhirVersionEnum.DSTU1) { client.loadPage().byUrl(url).andReturnDstu1Bundle().execute(); } else { @SuppressWarnings("unchecked") Class<? extends IBaseBundle> bundleType = (Class<? extends IBaseBundle>) context.getResourceDefinition("Bundle").getImplementingClass(); client.loadPage().byUrl(url).andReturnBundle(bundleType).execute(); } } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; String outcomeDescription = "Bundle Page"; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest); return "result"; } @RequestMapping(value = { "/read" }) public String actionRead(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) { addCommonParams(theReq, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor); RuntimeResourceDefinition def; try { def = getResourceType(theRequest, theReq); } catch (ServletException e) { theModel.put("errorMsg", e.toString()); return "resource"; } String id = StringUtils.defaultString(theReq.getParameter("id")); if (StringUtils.isBlank(id)) { theModel.put("errorMsg", "No ID specified"); return "resource"; } ResultType returnsResource = ResultType.RESOURCE; String versionId = StringUtils.defaultString(theReq.getParameter("vid")); String outcomeDescription; if (StringUtils.isBlank(versionId)) { versionId = null; outcomeDescription = "Read Resource"; } else { outcomeDescription = "VRead Resource"; } long start = System.currentTimeMillis(); try { IdDt resid = new IdDt(def.getName(), id, versionId); ourLog.info(logPrefix(theModel) + "Reading resource: {}", resid); if (resid.hasVersionIdPart()) { client.vread(def.getImplementingClass(), resid); } else { client.read(def.getImplementingClass(), resid); } } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest); return "result"; } @RequestMapping({ "/resource" }) public String actionResource(HttpServletRequest theServletRequest, final ResourceRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { IBaseResource conformance = addCommonParams(theServletRequest, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theServletRequest, getContext(theRequest), myConfig, interceptor); String resourceName = theRequest.getResource(); RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource()); TreeSet<String> includes = new TreeSet<String>(); TreeSet<String> revIncludes = new TreeSet<String>(); TreeSet<String> sortParams = new TreeSet<String>(); List<RestQuery> queries = new ArrayList<Conformance.RestQuery>(); boolean haveSearchParams = false; List<List<String>> queryIncludes = new ArrayList<List<String>>(); switch (theRequest.getFhirVersion(myConfig)) { case DSTU1: haveSearchParams = extractSearchParamsDstu1(conformance, resourceName, includes, sortParams, queries, haveSearchParams, queryIncludes); break; case DSTU2: haveSearchParams = extractSearchParamsDstu2(conformance, resourceName, includes, revIncludes, sortParams, queries, haveSearchParams, queryIncludes); break; case DSTU3: haveSearchParams = extractSearchParamsDstu3CapabilityStatement(conformance, resourceName, includes, revIncludes, sortParams, queries, haveSearchParams, queryIncludes); break; default: throw new IllegalStateException("Unknown FHIR version: " + theRequest.getFhirVersion(myConfig)); } theModel.put("includes", includes); theModel.put("revincludes", revIncludes); theModel.put("queries", queries); theModel.put("haveSearchParams", haveSearchParams); theModel.put("queryIncludes", queryIncludes); theModel.put("sortParams", sortParams); if (isNotBlank(theRequest.getUpdateId())) { String updateId = theRequest.getUpdateId(); String updateVid = defaultIfEmpty(theRequest.getUpdateVid(), null); IBaseResource updateResource = (IBaseResource) client.read(def.getImplementingClass(), new IdDt(resourceName, updateId, updateVid)); String updateResourceString = theRequest.newParser(getContext(theRequest)).setPrettyPrint(true).encodeResourceToString(updateResource); theModel.put("updateResource", updateResourceString); theModel.put("updateResourceId", updateId); } ourLog.info(logPrefix(theModel) + "Showing resource page: {}", resourceName); return "resource"; } @SuppressWarnings("unchecked") @RequestMapping(value = { "/search" }) public String actionSearch(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel) throws IOException { addCommonParams(theReq, theRequest, theModel); StringWriter clientCodeJsonStringWriter = new StringWriter(); JsonWriter clientCodeJsonWriter = new JsonWriter(clientCodeJsonStringWriter); clientCodeJsonWriter.beginObject(); clientCodeJsonWriter.name("action"); clientCodeJsonWriter.value("search"); clientCodeJsonWriter.name("base"); clientCodeJsonWriter.value((String) theModel.get("base")); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor); IUntypedQuery search = client.search(); IQuery query; if (isNotBlank(theReq.getParameter("resource"))) { try { query = search.forResource((Class<? extends IBaseResource>) getResourceType(theRequest, theReq).getImplementingClass()); } catch (ServletException e) { theModel.put("errorMsg", e.toString()); return "resource"; } clientCodeJsonWriter.name("resource"); clientCodeJsonWriter.value(theReq.getParameter("resource")); } else { query = search.forAllResources(); clientCodeJsonWriter.name("resource"); clientCodeJsonWriter.nullValue(); } if (client.isPrettyPrint()) { clientCodeJsonWriter.name("pretty"); clientCodeJsonWriter.value("true"); } else { clientCodeJsonWriter.name("pretty"); clientCodeJsonWriter.nullValue(); } if (client.getEncoding() != null) { clientCodeJsonWriter.name("format"); clientCodeJsonWriter.value(client.getEncoding().getRequestContentType()); } else { clientCodeJsonWriter.name("format"); clientCodeJsonWriter.nullValue(); } String outcomeDescription = "Search for Resources"; clientCodeJsonWriter.name("params"); clientCodeJsonWriter.beginArray(); int paramIdx = -1; while (true) { paramIdx++; String paramIdxString = Integer.toString(paramIdx); boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query, clientCodeJsonWriter); if (!shouldContinue) { break; } } clientCodeJsonWriter.endArray(); clientCodeJsonWriter.name("includes"); clientCodeJsonWriter.beginArray(); String[] incValues = theReq.getParameterValues(Constants.PARAM_INCLUDE); if (incValues != null) { for (String next : incValues) { if (isNotBlank(next)) { query.include(new Include(next)); clientCodeJsonWriter.value(next); } } } clientCodeJsonWriter.endArray(); clientCodeJsonWriter.name("revincludes"); clientCodeJsonWriter.beginArray(); String[] revIncValues = theReq.getParameterValues(Constants.PARAM_REVINCLUDE); if (revIncValues != null) { for (String next : revIncValues) { if (isNotBlank(next)) { query.revInclude(new Include(next)); clientCodeJsonWriter.value(next); } } } clientCodeJsonWriter.endArray(); String limit = theReq.getParameter("resource-search-limit"); if (isNotBlank(limit)) { if (!limit.matches("[0-9]+")) { theModel.put("errorMsg", "Search limit must be a numeric value."); return "resource"; } int limitInt = Integer.parseInt(limit); query.limitTo(limitInt); clientCodeJsonWriter.name("limit"); clientCodeJsonWriter.value(limit); } else { clientCodeJsonWriter.name("limit"); clientCodeJsonWriter.nullValue(); } String[] sort = theReq.getParameterValues("sort_by"); if (sort != null) { for (String next : sort) { if (isBlank(next)) { continue; } String direction = theReq.getParameter("sort_direction"); if ("asc".equals(direction)) { query.sort().ascending(new StringClientParam(next)); } else if ("desc".equals(direction)) { query.sort().descending(new StringClientParam(next)); } else { query.sort().defaultOrder(new StringClientParam(next)); } } } if (client.getFhirContext().getVersion().getVersion() != FhirVersionEnum.DSTU1) { query.returnBundle(client.getFhirContext().getResourceDefinition("Bundle").getImplementingClass()); } long start = System.currentTimeMillis(); ResultType returnsResource; try { ourLog.info(logPrefix(theModel) + "Executing a search"); query.execute(); returnsResource = ResultType.BUNDLE; } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest); clientCodeJsonWriter.endObject(); clientCodeJsonWriter.close(); String clientCodeJson = clientCodeJsonStringWriter.toString(); theModel.put("clientCodeJson", clientCodeJson); return "result"; } @RequestMapping(value = { "/transaction" }) public String actionTransaction(HttpServletRequest theServletRequest, final TransactionRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { addCommonParams(theServletRequest, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); FhirContext context = getContext(theRequest); GenericClient client = theRequest.newClient(theServletRequest, context, myConfig, interceptor); String body = preProcessMessageBody(theRequest.getTransactionBody()); try { if (body.startsWith("{")) { // JSON content } else if (body.startsWith("<")) { // XML content } else { theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding)."); return "home"; } } catch (DataFormatException e) { ourLog.warn("Failed to parse bundle", e); theModel.put("errorMsg", "Failed to parse transaction bundle body. Error was: " + e.getMessage()); return "home"; } ResultType returnsResource = ResultType.BUNDLE; long start = System.currentTimeMillis(); try { ourLog.info(logPrefix(theModel) + "Executing transaction"); client.transaction().withBundle(body).execute(); } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, "Transaction", interceptor, theRequest); return "result"; } @RequestMapping(value = { "/update" }) public String actionUpdate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "update"); return "result"; } @RequestMapping(value = { "/validate" }) public String actionValidate(final HttpServletRequest theReq, final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) { doActionCreateOrValidate(theReq, theRequest, theBindingResult, theModel, "validate"); return "result"; } private void doActionCreateOrValidate(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod) { boolean validate = "validate".equals(theMethod); addCommonParams(theReq, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor); client.setPrettyPrint(true); Class<? extends IBaseResource> type = null; // def.getImplementingClass(); if ("history-type".equals(theMethod)) { RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource()); type = (Class<? extends IBaseResource>) def.getImplementingClass(); } String body = validate ? theReq.getParameter("resource-validate-body") : theReq.getParameter("resource-create-body"); if (isBlank(body)) { theModel.put("errorMsg", "No message body specified"); return; } body = preProcessMessageBody(body); IBaseResource resource; try { if (body.startsWith("{")) { resource = getContext(theRequest).newJsonParser().parseResource(type, body); client.setEncoding(EncodingEnum.JSON); } else if (body.startsWith("<")) { resource = getContext(theRequest).newXmlParser().parseResource(type, body); client.setEncoding(EncodingEnum.XML); } else { theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding)."); return; } } catch (DataFormatException e) { ourLog.warn("Failed to parse resource", e); theModel.put("errorMsg", "Failed to parse message body. Error was: " + e.getMessage()); return; } String outcomeDescription; long start = System.currentTimeMillis(); ResultType returnsResource = ResultType.RESOURCE; outcomeDescription = ""; boolean update = false; try { if (validate) { outcomeDescription = "Validate Resource"; client.validate().resource(resource).prettyPrint().execute(); } else { String id = theReq.getParameter("resource-create-id"); if ("update".equals(theMethod)) { outcomeDescription = "Update Resource"; client.update(id, resource); update = true; } else { outcomeDescription = "Create Resource"; ICreateTyped create = client.create().resource(body); if (isNotBlank(id)) { create.withId(id); } create.execute(); } } } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription, interceptor, theRequest); try { if (validate) { ourLog.info(logPrefix(theModel) + "Validated resource of type " + getResourceType(theRequest, theReq).getName()); } else if (update) { ourLog.info(logPrefix(theModel) + "Updated resource of type " + getResourceType(theRequest, theReq).getName()); } else { ourLog.info(logPrefix(theModel) + "Created resource of type " + getResourceType(theRequest, theReq).getName()); } } catch (Exception e) { ourLog.warn("Failed to determine resource type from request", e); } } private void doActionHistory(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod, String theMethodDescription) { addCommonParams(theReq, theRequest, theModel); CaptureInterceptor interceptor = new CaptureInterceptor(); GenericClient client = theRequest.newClient(theReq, getContext(theRequest), myConfig, interceptor); String id = null; Class<? extends IBaseResource> type = null; // def.getImplementingClass(); if ("history-type".equals(theMethod)) { RuntimeResourceDefinition def = getContext(theRequest).getResourceDefinition(theRequest.getResource()); type = (Class<? extends IBaseResource>) def.getImplementingClass(); id = StringUtils.defaultString(theReq.getParameter("resource-history-id")); } DateTimeDt since = null; String sinceStr = theReq.getParameter("since"); if (isNotBlank(sinceStr)) { since = new DateTimeDt(sinceStr); } Integer limit = null; String limitStr = theReq.getParameter("limit"); if (isNotBlank(limitStr)) { limit = Integer.parseInt(limitStr); } ResultType returnsResource = ResultType.BUNDLE; long start = System.currentTimeMillis(); try { ourLog.info(logPrefix(theModel) + "Retrieving history for type {} ID {} since {}", new Object[] { type, id, since }); IHistory hist0 = client.history(); IHistoryUntyped hist1; if (isNotBlank(id)) { hist1 = hist0.onInstance(new IdDt(theRequest.getResource(), id)); } else if (type != null) { hist1 = hist0.onType(type); } else { hist1 = hist0.onServer(); } IHistoryTyped<?> hist2; if (client.getFhirContext().getVersion().getVersion() == FhirVersionEnum.DSTU1) { hist2 = hist1.andReturnDstu1Bundle(); } else { hist2 = hist1.andReturnBundle(client.getFhirContext().getResourceDefinition("Bundle").getImplementingClass(IBaseBundle.class)); } if (since != null) { hist2.since(since); } if (limit != null) { hist2.count(limit); } hist2.execute(); } catch (Exception e) { returnsResource = handleClientException(client, e, theModel); } long delay = System.currentTimeMillis() - start; processAndAddLastClientInvocation(client, returnsResource, theModel, delay, theMethodDescription, interceptor, theRequest); } private boolean extractSearchParamsDstu1(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> sortParams, List<RestQuery> queries, boolean haveSearchParams, List<List<String>> queryIncludes) { Conformance conformance = (Conformance) theConformance; for (Rest nextRest : conformance.getRest()) { for (RestResource nextRes : nextRest.getResource()) { if (nextRes.getType().getValue().equals(resourceName)) { for (StringDt next : nextRes.getSearchInclude()) { if (next.isEmpty() == false) { includes.add(next.getValue()); } } for (RestResourceSearchParam next : nextRes.getSearchParam()) { if (next.getType().getValueAsEnum() != SearchParamTypeEnum.COMPOSITE) { sortParams.add(next.getName().getValue()); } } if (nextRes.getSearchParam().size() > 0) { haveSearchParams = true; } } } for (RestQuery nextQuery : nextRest.getQuery()) { boolean queryMatchesResource = false; List<ExtensionDt> returnTypeExt = nextQuery.getUndeclaredExtensionsByUrl(ExtensionConstants.QUERY_RETURN_TYPE); if (returnTypeExt != null) { for (ExtensionDt nextExt : returnTypeExt) { if (resourceName.equals(nextExt.getValueAsPrimitive().getValueAsString())) { queries.add(nextQuery); queryMatchesResource = true; break; } } } if (queryMatchesResource) { ArrayList<String> nextQueryIncludes = new ArrayList<String>(); queryIncludes.add(nextQueryIncludes); List<ExtensionDt> includesExt = nextQuery.getUndeclaredExtensionsByUrl(ExtensionConstants.QUERY_ALLOWED_INCLUDE); if (includesExt != null) { for (ExtensionDt nextExt : includesExt) { nextQueryIncludes.add(nextExt.getValueAsPrimitive().getValueAsString()); } } } } } return haveSearchParams; } private boolean extractSearchParamsDstu2(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams, List<RestQuery> queries, boolean haveSearchParams, List<List<String>> queryIncludes) { ca.uhn.fhir.model.dstu2.resource.Conformance conformance = (ca.uhn.fhir.model.dstu2.resource.Conformance) theConformance; for (ca.uhn.fhir.model.dstu2.resource.Conformance.Rest nextRest : conformance.getRest()) { for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResource nextRes : nextRest.getResource()) { if (nextRes.getTypeElement().getValue().equals(resourceName)) { for (StringDt next : nextRes.getSearchInclude()) { if (next.isEmpty() == false) { includes.add(next.getValue()); } } for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam next : nextRes.getSearchParam()) { if (next.getTypeElement().getValueAsEnum() != ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum.COMPOSITE) { sortParams.add(next.getNameElement().getValue()); } } if (nextRes.getSearchParam().size() > 0) { haveSearchParams = true; } } else { // It's a different resource from the one we're searching, so // scan for revinclude candidates for (ca.uhn.fhir.model.dstu2.resource.Conformance.RestResourceSearchParam next : nextRes.getSearchParam()) { if (next.getTypeElement().getValueAsEnum() == ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum.REFERENCE) { for (BoundCodeDt<ResourceTypeEnum> nextTargetType : next.getTarget()) { if (nextTargetType.getValue().equals(resourceName)) { theRevIncludes.add(nextRes.getTypeElement().getValue() + ":" + next.getName()); } } } } } } } return haveSearchParams; } private boolean extractSearchParamsDstu3CapabilityStatement(IBaseResource theConformance, String resourceName, TreeSet<String> includes, TreeSet<String> theRevIncludes, TreeSet<String> sortParams, List<RestQuery> queries, boolean haveSearchParams, List<List<String>> queryIncludes) { CapabilityStatement conformance = (org.hl7.fhir.dstu3.model.CapabilityStatement) theConformance; for (CapabilityStatementRestComponent nextRest : conformance.getRest()) { for (CapabilityStatementRestResourceComponent nextRes : nextRest.getResource()) { if (nextRes.getTypeElement().getValue().equals(resourceName)) { for (StringType next : nextRes.getSearchInclude()) { if (next.isEmpty() == false) { includes.add(next.getValue()); } } for (CapabilityStatementRestResourceSearchParamComponent next : nextRes.getSearchParam()) { if (next.getTypeElement().getValue() != org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.COMPOSITE) { sortParams.add(next.getNameElement().getValue()); } } if (nextRes.getSearchParam().size() > 0) { haveSearchParams = true; } } else { // It's a different resource from the one we're searching, so // scan for revinclude candidates for (CapabilityStatementRestResourceSearchParamComponent next : nextRes.getSearchParam()) { if (next.getTypeElement().getValue() == org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE) { // for (CodeType nextTargetType : next.getTarget()) { // if (nextTargetType.getValue().equals(resourceName)) { // theRevIncludes.add(nextRes.getTypeElement().getValue() + ":" + next.getName()); // } // } } } } } } return haveSearchParams; } private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery<?> theQuery, JsonWriter theClientCodeJsonWriter) throws IOException { String nextName = theReq.getParameter("param." + paramIdxString + ".name"); if (isBlank(nextName)) { return false; } String nextQualifier = StringUtils.defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier")); String nextType = theReq.getParameter("param." + paramIdxString + ".type"); List<String> parts = new ArrayList<String>(); for (int i = 0; i < 5; i++) { parts.add(defaultString(theReq.getParameter("param." + paramIdxString + "." + i))); } List<String> values; boolean addToWhere = true; if ("token".equals(nextType)) { if (isBlank(parts.get(1))) { return true; } addToWhere = false; if (isBlank(parts.get(0))) { values = Collections.singletonList(parts.get(1)); theQuery.where(new TokenClientParam(nextName + nextQualifier).exactly().code(parts.get(1))); } else { values = Collections.singletonList(parts.get(0) + "|" + parts.get(1)); theQuery.where(new TokenClientParam(nextName + nextQualifier).exactly().systemAndCode(parts.get(0), parts.get(1))); } } else if ("date".equals(nextType)) { values = new ArrayList<String>(); if (isNotBlank(parts.get(1))) { values.add(StringUtils.join(parts.get(0), parts.get(1))); } if (isNotBlank(parts.get(3))) { values.add(StringUtils.join(parts.get(2), parts.get(3))); } if (values.isEmpty()) { return true; } } else if ("quantity".equals(nextType)) { values = new ArrayList<String>(); addToWhere = false; QuantityClientParam param = new QuantityClientParam(nextName); IMatches<IAndUnits> matcher; if ("~".equals(parts.get(0))) { matcher = param.approximately(); } else if ("=".equals(parts.get(0))) { matcher = param.exactly(); } else if (">=".equals(parts.get(0))) { matcher = param.greaterThanOrEquals(); } else if ("<=".equals(parts.get(0))) { matcher = param.lessThanOrEquals(); } else if (">".equals(parts.get(0))) { matcher = param.greaterThan(); } else if ("<".equals(parts.get(0))) { matcher = param.lessThan(); } else { throw new Error("Unknown qualifier: " + parts.get(0)); } IAndUnits number = matcher.number(parts.get(1)); if (isBlank(parts.get(3))) { theQuery.where(number.andNoUnits()); } else if (isBlank(parts.get(2))) { theQuery.where(number.andUnits(parts.get(3))); } else { theQuery.where(number.andUnits(parts.get(2), parts.get(3))); } values.add(parts.get(0) + parts.get(1) + "|" + parts.get(2) + "|" + parts.get(3)); if (values.isEmpty()) { return true; } } else { values = Collections.singletonList(StringUtils.join(parts, "")); if (isBlank(values.get(0))) { return true; } } for (String nextValue : values) { theClientCodeJsonWriter.beginObject(); theClientCodeJsonWriter.name("type"); theClientCodeJsonWriter.value(nextType); theClientCodeJsonWriter.name("name"); theClientCodeJsonWriter.value(nextName); theClientCodeJsonWriter.name("qualifier"); theClientCodeJsonWriter.value(nextQualifier); theClientCodeJsonWriter.name("value"); theClientCodeJsonWriter.value(nextValue); theClientCodeJsonWriter.endObject(); if (addToWhere) { theQuery.where(new StringClientParam(nextName + nextQualifier).matches().value(nextValue)); } } if (StringUtils.isNotBlank(theReq.getParameter("param." + paramIdxString + ".0.name"))) { handleSearchParam(paramIdxString + ".0", theReq, theQuery, theClientCodeJsonWriter); } return true; } }