Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b9ba57f
updating search parm registry
Aug 22, 2025
4405308
working code
Aug 25, 2025
df5e976
complete
Aug 26, 2025
dd0714c
adding a test
Aug 26, 2025
544ac53
adding changelog
Aug 26, 2025
5ba05cd
spotless
Aug 26, 2025
56a7a45
Merge branch 'master' into 7208-update-search-param-registry
Aug 26, 2025
e1c1db1
updating spvalidator
Aug 28, 2025
7cdc8b2
adding documentation
Aug 29, 2025
4180ad9
fixing tests
Aug 29, 2025
ad6a33a
fixing tests
Aug 29, 2025
a07afef
Merge branch 'master' into 7208-update-search-param-registry
Sep 2, 2025
7aebfd9
review fixes
Sep 2, 2025
af039b9
fixing more tests
Sep 2, 2025
8ab343c
fixing tests
Sep 2, 2025
ba0c5e9
adding a default that isn't a valid validation to preserve existing b…
Sep 2, 2025
0e11dc8
spotless
Sep 2, 2025
4e12b12
Merge branch 'master' into 7208-update-search-param-registry
Sep 3, 2025
9d2c65a
allow overwriting builtin sp
Sep 3, 2025
e9d5ac6
spotless
Sep 3, 2025
8167799
fxiing a bug
Sep 4, 2025
1fb6639
init
Sep 5, 2025
a979b90
updates
Sep 5, 2025
68fe4ec
spotless
Sep 5, 2025
6914bc5
documentation
Sep 5, 2025
d0e5226
updating changelog
Sep 8, 2025
da5da3e
adding some logging
Sep 8, 2025
3c3bf57
fixing it
Sep 8, 2025
7e319c0
updating api for hapi package cache manager
Sep 8, 2025
ea16c2e
spotless
Sep 8, 2025
59d1b4b
allowing some sorting
Sep 8, 2025
8a6618d
adding checked error
Sep 9, 2025
f475040
updating a test
Sep 9, 2025
e62c99f
all the new stuff
Sep 9, 2025
69a3c28
review points
Sep 9, 2025
56b5378
review fixes
Sep 9, 2025
25a2d12
spotless
Sep 9, 2025
1b63e07
added alidation fixmes
Sep 10, 2025
7f7484e
fixing a bug
Sep 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package ca.uhn.fhir.context;

import ca.uhn.fhir.model.api.IFhirVersion;

public class RuntimeResourceSource {
/**
* Enum defining the source of the SearchParameter
* (ie, where it's from)
*/
public enum SourceType {
/**
* Unknown source; a source should always be provided,
* but if one cannot be, specify UNKNOWN.
*/
UNKNOWN,
/**
* The Resource is built-into the base FHIR context.
*/
FHIR_CONTEXT,
/**
* The originating source is the DB (this was saved
* via an API call or the like).
*/
DATABASE,
/**
* The originating source is an IG.
* These are Implementation Guides from NPM
*/
NPM;
}

public static RuntimeResourceSource fhirContextSource(FhirContext theFhirContext) {
RuntimeResourceSource source = new RuntimeResourceSource();
source.setOriginatingSource(SourceType.FHIR_CONTEXT);
IFhirVersion fhirVersion = theFhirContext.getVersion();
source.setIGUrlAndVersion("FhirContext", fhirVersion.getVersion().getFhirVersionString());
return source;
}

public static RuntimeResourceSource databaseSource() {
RuntimeResourceSource source = new RuntimeResourceSource();
source.setOriginatingSource(SourceType.DATABASE);
return source;
}

public static RuntimeResourceSource npmSource(String theIG, String theIGVersion) {
RuntimeResourceSource source = new RuntimeResourceSource();
source.setOriginatingSource(SourceType.NPM);
source.setIGUrlAndVersion(theIG, theIGVersion);
return source;
}

/**
* The originating source of this RuntimeSearchParameter.
*/
private SourceType myOriginatingSource = SourceType.UNKNOWN;

/**
* The name of the source Implementation Guide
*/
private String myIGName;

/**
* The version of the IG
*/
private String myIGVersion;

public SourceType getOriginatingSource() {
return myOriginatingSource;
}

public void setOriginatingSource(SourceType theOriginatingSource) {
myOriginatingSource = theOriginatingSource;
}

public String getIGName() {
return myIGName;
}

public String getIGVersion() {
return myIGVersion;
}

public void setIGUrlAndVersion(String theIGUrl, String theVersion) {
myIGName = theIGUrl;
myIGVersion = theVersion;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import static org.apache.commons.lang3.StringUtils.trim;

public class RuntimeSearchParam {

private final IIdType myId;
private final Set<String> myBase;
private final String myDescription;
Expand All @@ -54,6 +55,7 @@ public class RuntimeSearchParam {
private final Set<String> myProvidesMembershipInCompartments;
private final RuntimeSearchParamStatusEnum myStatus;
private final String myUri;
private String myVersion;
private final Map<String, List<IBaseExtension<?, ?>>> myExtensions = new HashMap<>();
private final Map<String, String> myUpliftRefchains = new HashMap<>();
private final ComboSearchParamType myComboSearchParamType;
Expand All @@ -62,6 +64,12 @@ public class RuntimeSearchParam {
private IPhoneticEncoder myPhoneticEncoder;
private boolean myEnabledForSearching = true;

/**
* The source of this SP.
* We have it initialized so it's never null; but seeding services
* should populate this value
*/
private RuntimeResourceSource mySource = new RuntimeResourceSource();
/**
* Constructor
*/
Expand Down Expand Up @@ -185,6 +193,14 @@ public void setEnabledForSearching(boolean theEnabledForSearching) {
myEnabledForSearching = theEnabledForSearching;
}

public RuntimeResourceSource getSource() {
return mySource;
}

public void setSource(RuntimeResourceSource theSource) {
mySource = theSource;
}

public List<Component> getComponents() {
return myComponents;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.ClasspathUtil;
import ca.uhn.fhir.util.FhirTerser;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseBundle;
Expand Down Expand Up @@ -187,7 +186,6 @@ public <T extends IBaseResource> List<T> fetchAllSearchParameters() {
retVal = new ArrayList<>();
for (String searchParameterResource : mySearchParameterResources) {
try (InputStream inputStream = ClasspathUtil.loadResourceAsStream(searchParameterResource)) {
FhirTerser terser = myCtx.newTerser();
EncodingEnum encoding =
searchParameterResource.endsWith("json") ? EncodingEnum.JSON : EncodingEnum.XML;
List<IBaseResource> resources =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ca.uhn.fhir.model.npm;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonAutoDetect(
creatorVisibility = JsonAutoDetect.Visibility.NONE,
fieldVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE)
public class NpmPackageMetadataLiteJson {
/**
* The npm package name
*/
@JsonProperty("name")
private String myName;
/**
* The npm package version
*/
@JsonProperty("version")
private String myVersion;

public String getName() {
return myName;
}

public void setName(String theName) {
myName = theName;
}

public String getVersion() {
return myVersion;
}

public void setVersion(String theVersion) {
myVersion = theVersion;
}
}
68 changes: 68 additions & 0 deletions hapi-fhir-base/src/main/java/ca/uhn/fhir/util/NpmPackageUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ca.uhn.fhir.util;

import ca.uhn.fhir.context.RuntimeResourceSource;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.npm.NpmPackageMetadataLiteJson;
import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IBaseResource;

import java.util.Collections;
import java.util.List;

import static org.apache.commons.lang3.StringUtils.isBlank;

public class NpmPackageUtils {

public static final String LOADER_WITH_CACHE = "loaderWithCache";

/**
* Default install types
*/
public static List<String> DEFAULT_INSTALL_TYPES = Collections.unmodifiableList(Lists.newArrayList(
"NamingSystem",
"CodeSystem",
"ValueSet",
"StructureDefinition",
"ConceptMap",
"SearchParameter",
"Subscription"));

public static final String PKG_METADATA_KEY = "PKG_METADATA";

/**
* Adds package metadata to the resource for identification purposes downstream.
* @param theResource the resource
* @param thePkg the npm package (IG) it is from
*/
public static void addPackageMetadata(IBaseResource theResource, String thePkgName, String thePkgVersion) {
NpmPackageMetadataLiteJson metadata = new NpmPackageMetadataLiteJson();
metadata.setName(thePkgName);
metadata.setVersion(thePkgVersion);

theResource.setUserData(PKG_METADATA_KEY, JsonUtil.serialize(metadata));
}

/**
* Retrieves whatever npm pkg metadata is on this resource, or null if none is found.
* @param theResource resource that originated from an npm package
* @return the metadata about the pkg (or null if not available)
*/
public static NpmPackageMetadataLiteJson getPackageMetadata(IBaseResource theResource) {
String metadataJson = (String) theResource.getUserData(PKG_METADATA_KEY);
if (isBlank(metadataJson)) {
// metadata was not on this resource
return null;
}
return JsonUtil.deserialize(metadataJson, NpmPackageMetadataLiteJson.class);
}

public static boolean isFromNpmPackage(IBaseResource theResource) {
return theResource.getUserData(PKG_METADATA_KEY) != null;
}

public static void setSourceForSP(IBaseResource theResource, RuntimeSearchParam theSearchParam) {
assert isFromNpmPackage(theResource);
NpmPackageMetadataLiteJson metadata = getPackageMetadata(theResource);
theSearchParam.setSource(RuntimeResourceSource.npmSource(metadata.getName(), metadata.getVersion()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ public static List<RuntimeSearchParam> getAllPatientCompartmentRuntimeSearchPara
return getAllPatientCompartmentRuntimeSearchParams(runtimeResourceDefinition);
}

public static List<RuntimeSearchParam> getAllPatientCompartmenRuntimeSearchParams(FhirContext theFhirContext) {
public static List<RuntimeSearchParam> getAllPatientCompartmentRuntimeSearchParams(FhirContext theFhirContext) {
return theFhirContext.getResourceTypes().stream()
.flatMap(type ->
getAllPatientCompartmentRuntimeSearchParamsForResourceType(theFhirContext, type).stream())
Expand Down Expand Up @@ -420,6 +420,22 @@ private static boolean allParensHaveBeenClosed(String thePaths) {
return open == close;
}

/**
* Retrieves the first value in the provided field, or null or no such field exists or isn't populated.
* @param theTerser
* @param theSP
* @param theField
* @return
*/
public static IBase getFirstFieldValueOrNull(FhirTerser theTerser, IBaseResource theSP, String theField) {
List<IBase> fieldValues = theTerser.getValues(theSP, theField);

if (fieldValues != null && !fieldValues.isEmpty()) {
return fieldValues.get(0);
}
return null;
}

/**
* Given a FHIRPath expression which presumably addresses a FHIR reference or
* canonical reference element (i.e. a FHIRPath expression used in a "reference"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ default String fhirType() {
}

/**
* Retrieves any user suplied data in this element
* Retrieves any user supplied data in this element
*/
Object getUserData(String theName);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
type: add
issue: 7208
title: "Added the ability to add SearchParameters to a local cache
without having them added to the built in search parameters
or the database.
IValidationSupport can now also be used to read all SearchParameters
out of Npm packages (Implementation Guides).
"
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,8 @@
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.packages.IHapiPackageCacheManager;
import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc;
import ca.uhn.fhir.jpa.packages.JpaPackageCache;
import ca.uhn.fhir.jpa.packages.NpmJpaValidationSupport;
import ca.uhn.fhir.jpa.packages.PackageInstallerSvcImpl;
import ca.uhn.fhir.jpa.packages.util.PackageUtils;
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl;
Expand Down Expand Up @@ -184,7 +180,6 @@
import ca.uhn.fhir.jpa.util.PersistenceContextProvider;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain;
import ca.uhn.fhir.jpa.validation.ResourceLoaderImpl;
import ca.uhn.fhir.jpa.validation.ValidationSettings;
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.replacereferences.PreviousResourceVersionRestorer;
import ca.uhn.fhir.replacereferences.ReplaceReferencesPatchBundleSvc;
Expand Down Expand Up @@ -411,21 +406,6 @@ public IResourceLinkResolver daoResourceLinkResolver() {
return new JpaDaoResourceLinkResolver();
}

@Bean(name = PackageUtils.LOADER_WITH_CACHE)
public IHapiPackageCacheManager packageCacheManager() {
return new JpaPackageCache();
}

@Bean
public NpmJpaValidationSupport npmJpaValidationSupport() {
return new NpmJpaValidationSupport();
}

@Bean
public ValidationSettings validationSettings() {
return new ValidationSettings();
}

@Bean
public ISearchCacheSvc searchCacheSvc() {
return new DatabaseSearchCacheSvcImpl();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@
package ca.uhn.fhir.jpa.config;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.packages.IHapiPackageCacheManager;
import ca.uhn.fhir.jpa.packages.JpaPackageCache;
import ca.uhn.fhir.jpa.packages.NpmJpaValidationSupport;
import ca.uhn.fhir.jpa.packages.loader.PackageLoaderSvc;
import ca.uhn.fhir.jpa.packages.loader.PackageResourceParsingSvc;
import ca.uhn.fhir.jpa.validation.ValidationSettings;
import ca.uhn.fhir.util.NpmPackageUtils;
import org.hl7.fhir.utilities.npm.PackageServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -42,4 +47,19 @@ public PackageLoaderSvc packageLoaderSvc() {
public PackageResourceParsingSvc resourceParsingSvc(FhirContext theContext) {
return new PackageResourceParsingSvc(theContext);
}

@Bean(name = NpmPackageUtils.LOADER_WITH_CACHE)
public IHapiPackageCacheManager packageCacheManager() {
return new JpaPackageCache();
}

@Bean
public NpmJpaValidationSupport npmJpaValidationSupport() {
return new NpmJpaValidationSupport();
}

@Bean
public ValidationSettings validationSettings() {
return new ValidationSettings();
}
}
Loading
Loading