From 2c9ea8c1796810f19395f8d7af141f4277eae6ee Mon Sep 17 00:00:00 2001 From: Lee Surprenant Date: Mon, 21 Feb 2022 09:26:08 -0500 Subject: [PATCH] issue #1777 - extract compartment inclusion params even when filtered Signed-off-by: Lee Surprenant --- .../jdbc/impl/FHIRPersistenceJDBCImpl.java | 119 ++++++++++-------- .../fhir/search/parameters/ParametersMap.java | 27 ++++ .../search/parameters/ParametersUtil.java | 44 ++++++- .../com/ibm/fhir/search/util/SearchUtil.java | 35 ++++++ 4 files changed, 169 insertions(+), 56 deletions(-) diff --git a/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java b/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java index 08c248bed7d..a3c2e4f9d33 100644 --- a/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java +++ b/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java @@ -166,7 +166,6 @@ import com.ibm.fhir.search.exception.FHIRSearchException; import com.ibm.fhir.search.parameters.InclusionParameter; import com.ibm.fhir.search.parameters.QueryParameter; -import com.ibm.fhir.search.reference.value.CompartmentReference; import com.ibm.fhir.search.util.ReferenceValue; import com.ibm.fhir.search.util.ReferenceValue.ReferenceType; import com.ibm.fhir.search.util.SearchUtil; @@ -223,7 +222,7 @@ public class FHIRPersistenceJDBCImpl implements FHIRPersistence, SchemaNameSuppl // Enable use of legacy whole-system search parameters for the search request private final boolean legacyWholeSystemSearchParamsEnabled; - + // A list of payload persistence responses in case we have a rollback to clean up private final List payloadPersistenceResponses = new ArrayList<>(); @@ -377,7 +376,7 @@ public SingleResourceResult create(FHIRPersistenceContex // Create the new Resource DTO instance. com.ibm.fhir.persistence.jdbc.dto.Resource resourceDTO = - createResourceDTO(updatedResource.getClass(), logicalId, newVersionNumber, lastUpdated, updatedResource, + createResourceDTO(updatedResource.getClass(), logicalId, newVersionNumber, lastUpdated, updatedResource, getResourcePayloadKeyFromContext(context)); // The DAO objects are now created on-the-fly (not expensive to construct) and @@ -447,7 +446,7 @@ private void doCachePrefill() throws FHIRPersistenceException { /** * Creates and returns a data transfer object (DTO) with the contents of the passed arguments. - * + * * @param resourceType * @param logicalId * @param newVersionNumber @@ -582,9 +581,9 @@ public SingleResourceResult update(FHIRPersistenceContex // Persist the Resource DTO. resourceDao.setPersistenceContext(context); ExtractedSearchParameters searchParameters = this.extractSearchParameters(resource, resourceDTO); - resourceDao.insert(resourceDTO, searchParameters.getParameters(), searchParameters.getParameterHashB64(), + resourceDao.insert(resourceDTO, searchParameters.getParameters(), searchParameters.getParameterHashB64(), parameterDao, context.getIfNoneMatch()); - + if (log.isLoggable(Level.FINE)) { if (resourceDTO.getInteractionStatus() == InteractionStatus.IF_NONE_MATCH_EXISTED) { log.fine("If-None-Match: Existing FHIR Resource '" + resourceDTO.getResourceType() + "/" + resourceDTO.getLogicalId() + "' id=" + resourceDTO.getId() @@ -967,12 +966,12 @@ private List runIncludeQuery(Class lrIds = includeDTOs.stream() .map(r -> Long.toString(r.getLogicalResourceId())).collect(Collectors.toSet()); Map> resultMap = queryResultMap.computeIfAbsent(iterationLevel, k -> new HashMap<>()); - + final String targetResourceType = SearchConstants.INCLUDE.equals(includeType) ? inclusionParm.getSearchParameterTargetType() : inclusionParm.getJoinResourceType(); Set resultLogicalResourceIds = resultMap.computeIfAbsent(targetResourceType, k -> new HashSet<>()); resultLogicalResourceIds.addAll(lrIds); - + // Because the resultLogicalResourceIds may contain resources of different types, we need // to make sure the resourceTypeId is properly marked on each DTO. We could've selected // that from the database, but we have the info here, so it's easy to inject it and avoid @@ -1022,7 +1021,7 @@ private FHIRPersistenceNotSupportedException buildNotSupportedException(String m } @Override - public void delete(FHIRPersistenceContext context, Class resourceType, String logicalId, int versionId, + public void delete(FHIRPersistenceContext context, Class resourceType, String logicalId, int versionId, com.ibm.fhir.model.type.Instant lastUpdated) throws FHIRPersistenceException { final String METHODNAME = "delete"; log.entering(CLASSNAME, METHODNAME); @@ -1428,7 +1427,7 @@ protected List buildSortedResourceDT * @throws FHIRPersistenceDBConnectException */ private List getResourceDTOs(ResourceDAO resourceDao, - Class resourceType, List sortedIdList, boolean includeResourceData) + Class resourceType, List sortedIdList, boolean includeResourceData) throws FHIRPersistenceDataAccessException, FHIRPersistenceDBConnectException { return resourceDao.searchByIds(resourceType.getSimpleName(), sortedIdList, includeResourceData); @@ -1456,18 +1455,18 @@ protected List> convertResourceDTOList(Resour // TODO Linear fetch of a large number of resources will extend response times. Need // to look into batch or parallel fetch requests ResourceResult resourceResult = convertResourceDTOToResourceResult(resourceDTO, resourceType, elements, includeResourceData); - + // Check to make sure we got a Resource if we asked for it and expect there to be one if (resourceResult.getResource() == null && includeResourceData && !resourceResult.isDeleted()) { String resourceTypeName = getResourceTypeInfo(resourceDTO); if (resourceTypeName == null) { resourceTypeName = resourceType.getSimpleName(); } - throw new FHIRPersistenceException("convertResourceDTO returned no resource for '" + throw new FHIRPersistenceException("convertResourceDTO returned no resource for '" + resourceTypeName + "/" + resourceDTO.getLogicalId() + "'"); } - // Note that if the resource has been erased or was not fetched on purpose, + // Note that if the resource has been erased or was not fetched on purpose, // ResourceResult.resource will be null and the caller will need to take this // into account resourceResults.add(resourceResult); @@ -1813,24 +1812,42 @@ protected void addCompartmentParams(List allParameters, log.fine("Processing compartment parameters for resourceType: " + resourceType); } Map> compartmentRefParams = CompartmentUtil.getCompartmentParamsForResourceType(resourceType); - Map> compartmentMap = SearchUtil.extractCompartmentParameterValues(fhirResource, compartmentRefParams); + Map collectedEPV = allParameters.stream() + .collect(Collectors.toMap(epv -> epv.getName(), epv -> epv)); + + for (Entry> entry : compartmentRefParams.entrySet()) { + String param = entry.getKey(); + Set compartments = entry.getValue(); + + ExtractedParameterValue epv = collectedEPV.get(param); + // If the parameter has no corresponding extracted value, log a warning and continue + if (epv == null) { + if (log.isLoggable(Level.FINE)) { + log.fine("Compartment inclusion param " + param + " has no value"); + } + continue; + } + + if (!(epv instanceof ReferenceParmVal)) { + log.warning("Skipping compartment inclusion param " + param + "; expected ReferenceParmVal but found " + + epv.getClass().getSimpleName()); + continue; + } - for (Map.Entry> entry: compartmentMap.entrySet()) { - final String compartmentName = entry.getKey(); - final String parameterName = CompartmentUtil.makeCompartmentParamName(compartmentName); + ReferenceValue rv = ((ReferenceParmVal) epv).getRefValue(); + if (rv != null && rv.getType() == ReferenceType.LITERAL_RELATIVE + && compartments.contains(rv.getTargetResourceType())) { + String internalCompartmentParamName = CompartmentUtil.makeCompartmentParamName(rv.getTargetResourceType()); - // Create a reference parameter value for each CompartmentReference extracted from the resource - for (CompartmentReference compartmentRef: entry.getValue()) { + // create a copy of the extracted parameter value but with the new internal compartment parameter name ReferenceParmVal pv = new ReferenceParmVal(); - pv.setName(parameterName); + pv.setName(internalCompartmentParamName); pv.setResourceType(resourceType); - - // ReferenceType doesn't really matter here, but LITERAL_RELATIVE is appropriate - ReferenceValue rv = new ReferenceValue(compartmentName, compartmentRef.getReferenceResourceValue(), ReferenceType.LITERAL_RELATIVE, null); pv.setRefValue(rv); if (log.isLoggable(Level.FINE)) { - log.fine("Adding compartment reference parameter: [" + resourceType + "] "+ parameterName + " = " + rv.getTargetResourceType() + "/" + rv.getValue()); + log.fine("Adding compartment reference parameter: [" + resourceType + "] " + + internalCompartmentParamName + " = " + rv.getTargetResourceType() + "/" + rv.getValue()); } allParameters.add(pv); } @@ -1972,7 +1989,7 @@ public OperationOutcome getHealth() throws FHIRPersistenceException { throw fx; } } - + /** * Checks to make sure the installed schema matches the version we expect * @param connection @@ -1991,7 +2008,7 @@ private boolean checkSchemaIsCurrent(Connection connection) throws SQLException, // doesn't exist versionId = -1; } - + // compare what's in the database with the latest FhirSchemaVersion. For now, // we allow the database schema to be equal to or ahead of the latest schema known // to this instance. This helps with rolling deploys. @@ -2004,12 +2021,12 @@ private boolean checkSchemaIsCurrent(Connection connection) throws SQLException, result = false; // not supported - database needs to be updated } else if (versionId > latest.vid()) { // the database has been updated, but this is the old code still running - log.warning("Deployment update required: database schema version [" + versionId + log.warning("Deployment update required: database schema version [" + versionId + "] is newer than code schema version [" + latest.vid() + "]"); result = true; // this is OK } else if (versionId < latest.vid()) { // the code is newer than the database schema - log.severe("Schema update required: database schema version [" + versionId + log.severe("Schema update required: database schema version [" + versionId + "] is older than code schema version [" + latest.vid() + "]"); result = false; // not supported - database needs to be updated } else { @@ -2062,7 +2079,7 @@ protected List convertResourceDTOList(List T convertResourceDTO(com.ibm.fhir.persistence.jdbc. rowResourceTypeName = resourceType.getSimpleName(); resourceTypeId = getResourceTypeId(resourceType); } - + // If a specific version of a resource has been deleted using $erase, it // is possible for the result here to be null. result = payloadPersistence.readResource(resourceType, rowResourceTypeName, resourceTypeId, resourceDTO.getLogicalId(), resourceDTO.getVersionId(), resourceDTO.getResourcePayloadKey(), elements); @@ -2120,7 +2137,7 @@ private T convertResourceDTO(com.ibm.fhir.persistence.jdbc. // resource doesn't exist result = null; } - + log.exiting(CLASSNAME, METHODNAME); return result; } @@ -2131,7 +2148,7 @@ private ResourceResult convertResourceDTOToResourceResul log.entering(CLASSNAME, METHODNAME); Objects.requireNonNull(resourceDTO, "resourceDTO must be not null"); T resource; - + if (includeData) { if (this.payloadPersistence != null) { // The payload needs to be read from the FHIRPayloadPersistence impl. If this is @@ -2145,7 +2162,7 @@ private ResourceResult convertResourceDTOToResourceResul rowResourceTypeName = resourceType.getSimpleName(); resourceTypeId = getResourceTypeId(resourceType); } - + // If a specific version of a resource has been deleted using $erase, it // is possible for the result here to be null. resource = payloadPersistence.readResource(resourceType, rowResourceTypeName, resourceTypeId, resourceDTO.getLogicalId(), resourceDTO.getVersionId(), resourceDTO.getResourcePayloadKey(), elements); @@ -2175,7 +2192,7 @@ private ResourceResult convertResourceDTOToResourceResul } else { resource = null; } - + // Note that resource may be null. We return a ResourceResult so we can // communicate back the type/id/version information even if we didn't get // an actual resource object @@ -2192,7 +2209,7 @@ private ResourceResult convertResourceDTOToResourceResul builder.resource(resource); // can be null builder.version(resourceDTO.getVersionId()); builder.lastUpdated(resourceDTO.getLastUpdated().toInstant()); - + log.exiting(CLASSNAME, METHODNAME); return builder.build(); } @@ -2206,7 +2223,7 @@ private ResourceResult convertResourceDTOToResourceResul * but the value cannot be found in the cache * @return */ - private String getResourceTypeInfo(com.ibm.fhir.persistence.jdbc.dto.Resource resourceDTO) + private String getResourceTypeInfo(com.ibm.fhir.persistence.jdbc.dto.Resource resourceDTO) throws FHIRPersistenceException { final String result; // resource type name needs to be derived from the resourceTypeId returned by the DB select query @@ -2279,7 +2296,7 @@ private OperationOutcome buildOKOperationOutcome() { private OperationOutcome buildErrorOperationOutcome() { return FHIRUtil.buildOperationOutcome("The database connection was not valid", IssueType.NO_STORE, IssueSeverity.ERROR); } - + private OperationOutcome buildSchemaVersionErrorOperationOutcome() { return FHIRUtil.buildOperationOutcome("The database schema version is old", IssueType.NO_STORE, IssueSeverity.ERROR); } @@ -2566,7 +2583,7 @@ private ParameterTransactionDataImpl getTransactionDataForDatasource(String data return result; } - + /** * Callback from TransactionData when a transaction has been rolled back * @param payloadPersistenceResponses an immutable list of {@link PayloadPersistenceResponse} @@ -2581,7 +2598,7 @@ private void handleRollback() { for (PayloadPersistenceResponse ppr: payloadPersistenceResponses) { try { log.fine(() -> "tx rollback - deleting payload: " + ppr.toString()); - payloadPersistence.deletePayload(ppr.getResourceTypeName(), ppr.getResourceTypeId(), + payloadPersistence.deletePayload(ppr.getResourceTypeName(), ppr.getResourceTypeId(), ppr.getLogicalId(), ppr.getVersionId(), ppr.getResourcePayloadKey()); } catch (Exception x) { // Nothing more we can do other than log the issue. Any rows we can't process @@ -2591,7 +2608,7 @@ private void handleRollback() { log.log(Level.SEVERE, "rollback failed to delete payload: " + ppr.toString(), x); } } - + payloadPersistenceResponses.clear(); } @@ -2657,7 +2674,7 @@ public ResourcePayload fetchResourcePayloads(Class resourceT @Override public List changes(int resourceCount, java.time.Instant sinceLastModified, java.time.Instant beforeLastModified, - Long changeIdMarker, List resourceTypeNames, boolean excludeTransactionTimeoutWindow, HistorySortOrder historySortOrder) + Long changeIdMarker, List resourceTypeNames, boolean excludeTransactionTimeoutWindow, HistorySortOrder historySortOrder) throws FHIRPersistenceException { try (Connection connection = openConnection()) { doCachePrefill(connection); @@ -2672,7 +2689,7 @@ public List changes(int resourceCount, java.time.Instan resourceTypeIds = null; // no filter on resource type } IDatabaseTranslator translator = FHIRResourceDAOFactory.getTranslatorForFlavor(connectionStrategy.getFlavor()); - FetchResourceChangesDAO dao = new FetchResourceChangesDAO(translator, schemaNameSupplier.getSchemaForRequestContext(connection), + FetchResourceChangesDAO dao = new FetchResourceChangesDAO(translator, schemaNameSupplier.getSchemaForRequestContext(connection), resourceCount, sinceLastModified, beforeLastModified, changeIdMarker, resourceTypeIds, excludeTransactionTimeoutWindow, historySortOrder); return dao.run(connection); @@ -2695,11 +2712,11 @@ public ResourceEraseRecord erase(EraseDTO eraseDto) throws FHIRPersistenceExcept doCachePrefill(connection); IDatabaseTranslator translator = FHIRResourceDAOFactory.getTranslatorForFlavor(connectionStrategy.getFlavor()); IResourceReferenceDAO rrd = makeResourceReferenceDAO(connection); - EraseResourceDAO eraseDao = new EraseResourceDAO(connection, FhirSchemaConstants.FHIR_ADMIN, translator, - schemaNameSupplier.getSchemaForRequestContext(connection), + EraseResourceDAO eraseDao = new EraseResourceDAO(connection, FhirSchemaConstants.FHIR_ADMIN, translator, + schemaNameSupplier.getSchemaForRequestContext(connection), connectionStrategy.getFlavor(), this.cache, rrd); long eraseResourceGroupId = eraseDao.erase(eraseRecord, eraseDto); - + // If offloading is enabled, we need to remove the corresponding offloaded resource payloads if (isOffloadingSupported()) { erasePayloads(eraseDao, eraseResourceGroupId); @@ -2707,7 +2724,7 @@ public ResourceEraseRecord erase(EraseDTO eraseDto) throws FHIRPersistenceExcept // clean up the erased_resources records because they're no longer needed eraseDao.clearErasedResourcesInGroup(eraseResourceGroupId); } - + } catch(FHIRPersistenceResourceNotFoundException e) { throw e; } catch(FHIRPersistenceException e) { @@ -2750,7 +2767,7 @@ private void erasePayloads(EraseResourceDAO dao, long erasedResourceGroupId) thr for (ErasedResourceRec rec: recs) { erasePayload(rec); } - + // If the above loop completed without throwing an exception, we can safely // remove all the records in the group. If an exception was thrown (because // the offload persistence layer was not accessible), don't delete right now @@ -2768,7 +2785,7 @@ private void erasePayload(ErasedResourceRec rec) throws FHIRPersistenceException if (resourceType == null) { throw new FHIRPersistenceException("Resource type not found in cache for resourceTypeId=" + rec.getResourceTypeId()); } - + // Note that if versionId is null, it means delete all known versions // The resourcePayloadKey is always null here, because the intention // for erase is to delete all instances of the record (in the rare case @@ -2851,15 +2868,15 @@ public List readResourcesForRecords(List reco for (ResourceChangeLogRecord r: records) { Class resourceType = ModelSupport.getResourceType(r.getResourceTypeName()); Resource resource = readResourceForRecord(readContext, r, resourceType); - - // We add the resource even if it's null because we want to keep the + + // We add the resource even if it's null because we want to keep the // list in alignment with the records list. A null might be returned // if the resource has been erased (hard delete). result.add(resource); } return result; } - + /** * Read the resource version for the given ResourceChangeLogRecord * @param diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersMap.java b/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersMap.java index 541f5d56af7..20e18dd5f9f 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersMap.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersMap.java @@ -30,6 +30,8 @@ public class ParametersMap { private final Map codeMap; private final Map canonicalMap; + private final Map inclusionParamMap; + /** * Construct a ParametersMap from a Bundle of SearchParameter */ @@ -37,6 +39,10 @@ public ParametersMap() { // LinkedHashMaps to preserve insertion order codeMap = new LinkedHashMap<>(); canonicalMap = new LinkedHashMap<>(); + + // Inclusion parameters are stored separately because they may be internal-only + // i.e. not externally searchable except through compartment search + inclusionParamMap = new LinkedHashMap<>(); } /** @@ -82,6 +88,23 @@ public void insert(String code, SearchParameter parameter) { } } + /** + * @param code + * @param parameter + * @param compartments + * @implSpec Any existing parameters will be replaced and a warning will be logged; last insert wins + */ + public void insertInclusionParam(String code, SearchParameter parameter, Set compartments) { + Objects.requireNonNull(code, "cannot insert a null code"); + Objects.requireNonNull(parameter, "cannot insert a null parameter"); + + if (inclusionParamMap.containsKey(code)) { + SearchParameter previous = inclusionParamMap.get(code); + logParamConflict("inclusion criteria '" + code + "'", parameter, ParametersUtil.getCanonicalUrl(parameter), previous); + } + inclusionParamMap.put(code, parameter); + } + private void logParamConflict(String distinguisher, SearchParameter parameter, String canonical, SearchParameter previous) { if (previous.getExpression().equals(parameter.getExpression())) { if (log.isLoggable(Level.FINE)) { @@ -117,6 +140,10 @@ public SearchParameter lookupByCanonical(String searchParameterCanonical) { return canonicalMap.get(searchParameterCanonical); } + public SearchParameter getInclusionParam(String searchParameterCode) { + return inclusionParamMap.get(searchParameterCode); + } + public Collection values() { // use List to preserve order return Collections.unmodifiableList(canonicalMap.entrySet().stream() diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersUtil.java b/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersUtil.java index 78aecc0fd7b..a4240d344aa 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersUtil.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/parameters/ParametersUtil.java @@ -29,6 +29,7 @@ import com.ibm.fhir.model.util.ModelSupport; import com.ibm.fhir.registry.FHIRRegistry; import com.ibm.fhir.search.SearchConstants; +import com.ibm.fhir.search.compartment.CompartmentUtil; import com.ibm.fhir.search.exception.SearchExceptionUtil; /** @@ -175,7 +176,9 @@ private static Map computeTenantSPs(PropertyGroup rsrcsGr } } - addWildcardParams(paramMapsByType, resourceTypesWithWildcardParams, configuredCodes); + addWildcardAndCompartmentParams(paramMapsByType, resourceTypesWithWildcardParams, configuredCodes); + +// addCompartmentParams(paramMapsByType, resourceTypesWithWildcardParams, configuredCodes); return Collections.unmodifiableMap(paramMapsByType); } @@ -192,6 +195,9 @@ private static ParametersMap getExplicitParams(Set resourceTypesWithWild if (spGroup != null) { List spEntries = spGroup.getProperties(); if (spEntries != null && !spEntries.isEmpty()) { + + Map> compartmentParamToCompartment = CompartmentUtil.getCompartmentParamsForResourceType(resourceType); + for (PropertyEntry spEntry : spEntries) { String code = spEntry.getName(); if (SearchConstants.WILDCARD.equals(code)) { @@ -201,8 +207,14 @@ private static ParametersMap getExplicitParams(Set resourceTypesWithWild .getResource((String)spEntry.getValue(), SearchParameter.class); if (sp != null) { paramMap.insert(code, sp); + + // If this param is an inclusion criteria for one or more compartments + if (compartmentParamToCompartment.containsKey(code)) { + paramMap.insertInclusionParam(code, sp, compartmentParamToCompartment.get(code)); + } } else { - log.warning("Search parameter '" + code + "' with the configured url '" + spEntry.getValue() + "' for resourceType '" + resourceType + "' could not be found."); + log.warning("Search parameter '" + code + "' with the configured url '" + spEntry.getValue() + + "' for resourceType '" + resourceType + "' could not be found."); } } } @@ -215,10 +227,11 @@ private static ParametersMap getExplicitParams(Set resourceTypesWithWild return paramMap; } - private static void addWildcardParams(Map paramMapsByType, + private static void addWildcardAndCompartmentParams(Map paramMapsByType, Set resourceTypesWithWildcardParams, Map> configuredCodes) { for (SearchParameter sp : getAllSearchParameters()) { + String code = sp.getCode().getValue(); // For each resource type this search parameter applies to for (ResourceType resourceType : sp.getBase()) { @@ -232,17 +245,38 @@ private static void addWildcardParams(Map paramMapsByType // Only add it if the code wasn't explicitly configured in fhir-server-config Set configuredCodesForType = configuredCodes.get(resourceType.getValue()); - if (configuredCodesForType != null && configuredCodesForType.contains(sp.getCode().getValue())) { + if (configuredCodesForType != null && configuredCodesForType.contains(code)) { if (log.isLoggable(Level.FINE)) { String canonical = getCanonicalUrl(sp); log.fine("Skipping search parameter '" + canonical + "' because code '" + sp.getCode().getValue() + "' is already configured."); } } else { - paramMap.insert(sp.getCode().getValue(), sp); + paramMap.insert(code, sp); } } + // If this param is an inclusion criteria for one or more compartments + Map> compartmentParamToCompartment = CompartmentUtil.getCompartmentParamsForResourceType(resourceType.getValue()); + if (compartmentParamToCompartment.containsKey(code)) { + ParametersMap paramMap = paramMapsByType.get(resourceType.getValue()); + if (paramMap == null) { + paramMap = new ParametersMap(); + paramMapsByType.put(resourceType.getValue(), paramMap); + } + + // Only add it if the code wasn't explicitly configured in fhir-server-config + Set configuredCodesForType = configuredCodes.get(resourceType.getValue()); + if (configuredCodesForType != null && configuredCodesForType.contains(code)) { + if (log.isLoggable(Level.FINE)) { + String canonical = getCanonicalUrl(sp); + log.fine("Skipping compartment inclusion parameter '" + canonical + "' because code '" + + sp.getCode().getValue() + "' is already configured."); + } + } else { + paramMap.insertInclusionParam(code, sp, compartmentParamToCompartment.get(code)); + } + } } } } diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java b/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java index 130da8f15cd..3b4a4334bc3 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java @@ -159,6 +159,33 @@ public static void init() { ParametersUtil.init(); } +// /** +// * @param resourceType +// * @param code +// * @return the SearchParameter for type {@code resourceType} with code {@code code} or null if it doesn't exist +// * @throws Exception +// */ +// public static SearchParameter getInclusionParameter(String resourceType, String code) { +// if (code == null) { +// return null; +// } +// String tenantId = FHIRRequestContext.get().getTenantId(); +// Map paramsByResourceType = ParametersUtil.getTenantSPs(tenantId); +// +// // First try the passed resourceType, then fall back to the Resource resourceType (for whole system params) +// for (String type : new String[]{resourceType, FHIRConfigHelper.RESOURCE_RESOURCE}) { +// ParametersMap parametersMap = paramsByResourceType.get(type); +// if (parametersMap != null) { +// SearchParameter searchParam = parametersMap.lookupByCode(code); +// if (searchParam != null) { +// return searchParam; +// } +// } +// } +// +// return null; +// } + /** * @param resourceType * @param code @@ -319,6 +346,14 @@ public static Map> extractParameterValues(Re Map parameters = getSearchParameters(resourceType.getSimpleName()); + for (String inclusionParamName : CompartmentUtil.getCompartmentParamsForResourceType(resourceType.getSimpleName()).keySet()) { + if (!parameters.containsKey(inclusionParamName)) { + String tenantId = FHIRRequestContext.get().getTenantId(); + ParametersMap parametersMap = ParametersUtil.getTenantSPs(tenantId).get(resourceType.getSimpleName()); + parameters.put(inclusionParamName, parametersMap.getInclusionParam(inclusionParamName)); + } + } + for (Entry parameterEntry : parameters.entrySet()) { String code = parameterEntry.getKey(); SearchParameter parameter = parameterEntry.getValue();