Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 18 additions & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<properties>
<java.version>17</java.version>
<hapi.fhir.jpa.server.starter.revision>2</hapi.fhir.jpa.server.starter.revision>
<hapi.fhir.jpa.server.starter.revision>3</hapi.fhir.jpa.server.starter.revision>
<clinical-reasoning.version>3.26.0</clinical-reasoning.version>
</properties>

Expand Down Expand Up @@ -60,6 +60,16 @@

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>${spring_boot_version}</version>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
</dependency>

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
Expand Down Expand Up @@ -265,12 +275,7 @@
<artifactId>moment</artifactId>
</dependency>

<!-- The following dependencies are only needed for automated unit tests, you do not neccesarily need them to run the example. -->
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<scope>test</scope>
</dependency>


<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
Expand Down Expand Up @@ -386,17 +391,15 @@
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp</artifactId>
<version>1.0.2</version>
<version>1.1.0-M1</version>
</dependency>
<!--
This will be included as well as using Spring Automatic Configuration
once spring-ai and io.modelcontextprotocol.sdk are on par
-->
<!--<dependency>

<!--implementation("org.springframework.ai:spring-ai-starter-mcp-server-webmvc:1.1.0-M1")-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
<version>1.0.2</version>
</dependency>-->
<version>1.1.0-M1</version>
</dependency>

<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2025 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.jpa.provider;

import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.RawParam;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.DateAndListParam;
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;

import java.util.List;
import java.util.Map;

// Can be removed when https://github.com/hapifhir/hapi-fhir/issues/7255 is resolved
public abstract class BaseJpaResourceProviderObservation<T extends IBaseResource> extends BaseJpaResourceProvider<T> {

/**
* Observation/$lastn
*/
@Operation(name = JpaConstants.OPERATION_LASTN, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET)
public IBundleProvider observationLastN(
jakarta.servlet.http.HttpServletRequest theServletRequest,
jakarta.servlet.http.HttpServletResponse theServletResponse,
ca.uhn.fhir.rest.api.server.RequestDetails theRequestDetails,
@Description(
formalDefinition =
"Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
@OperationParam(name = Constants.PARAM_COUNT, typeName = "unsignedInt")
IPrimitiveType<Integer> theCount,
@Description(shortDefinition = "The classification of the type of observation")
@OperationParam(name = "category")
TokenAndListParam theCategory,
@Description(shortDefinition = "The code of the observation type") @OperationParam(name = "code")
TokenAndListParam theCode,
@Description(shortDefinition = "The effective date of the observation") @OperationParam(name = "date")
DateAndListParam theDate,
@Description(shortDefinition = "The subject that the observation is about (if patient)")
@OperationParam(name = "patient")
ReferenceAndListParam thePatient,
@Description(shortDefinition = "The subject that the observation is about")
@OperationParam(name = "subject")
ReferenceAndListParam theSubject,
@Description(shortDefinition = "The maximum number of observations to return for each observation code")
@OperationParam(name = "max", typeName = "integer", min = 0, max = 1)
IPrimitiveType<Integer> theMax,
@RawParam Map<String, List<String>> theAdditionalRawParams) {
startRequest(theServletRequest);
try {
SearchParameterMap paramMap = new SearchParameterMap();
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_CATEGORY, theCategory);
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_CODE, theCode);
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_DATE, theDate);
if (thePatient != null) {
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_PATIENT, thePatient);
}
if (theSubject != null) {
paramMap.add(org.hl7.fhir.r4.model.Observation.SP_SUBJECT, theSubject);
}
if (theMax != null) {
paramMap.setLastNMax(theMax.getValue());

/**
* The removal of the original raw parameter is required as every implementing class
* has the "Observation" resource class defined. For this resource, the max parameter
* is not supported and thus has to be removed before the use of "translateRawParameters".
*/
if (theAdditionalRawParams != null) theAdditionalRawParams.remove("max");
}
if (theCount != null) {
paramMap.setCount(theCount.getValue());
}

getDao().translateRawParameters(theAdditionalRawParams, paramMap);

return ((IFhirResourceDaoObservation<?>) getDao())
.observationsLastN(paramMap, theRequestDetails, theServletResponse);
} finally {
endRequest(theServletRequest);
}
}
}
3 changes: 1 addition & 2 deletions src/main/java/ca/uhn/fhir/jpa/starter/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
Expand All @@ -26,7 +25,7 @@
import org.springframework.context.annotation.Import;

@ServletComponentScan(basePackageClasses = {RestfulServer.class})
@SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class, ThymeleafAutoConfiguration.class})
@SpringBootApplication(exclude = {ThymeleafAutoConfiguration.class})
@Import({
StarterCrR4Config.class,
StarterCrDstu3Config.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.starter.annotations;

import ca.uhn.fhir.jpa.starter.AppProperties;
import org.springframework.boot.context.properties.bind.Binder;
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
Expand All @@ -10,9 +10,7 @@ public class OnImplementationGuidesPresent implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {

AppProperties config = Binder.get(conditionContext.getEnvironment())
.bind("hapi.fhir", AppProperties.class)
.orElse(null);
AppProperties config = EnvironmentHelper.getConfiguration(conditionContext, "hapi.fhir", AppProperties.class);
if (config == null) return false;
if (config.getImplementationGuides() == null) return false;
return !config.getImplementationGuides().isEmpty();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import ca.uhn.fhir.jpa.model.config.SubscriptionSettings;
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
import ca.uhn.fhir.jpa.starter.AppProperties;
import ca.uhn.fhir.jpa.starter.elastic.ElasticsearchBootSvcImpl;
import ca.uhn.fhir.jpa.starter.util.JpaHibernatePropertiesProvider;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender;
Expand All @@ -19,10 +20,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.*;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;

Expand All @@ -36,6 +34,7 @@
*/
@Configuration
@EnableTransactionManagement
@Import(ElasticsearchBootSvcImpl.class)
public class FhirServerConfigCommon {

private static final Logger ourLog = LoggerFactory.getLogger(FhirServerConfigCommon.class);
Expand Down Expand Up @@ -274,7 +273,15 @@ public JpaStorageSettings jpaStorageSettings(AppProperties appProperties) {
ourLog.debug("Server configured to Store Meta Source: {}", appProperties.getStore_meta_source_information());
jpaStorageSettings.setStoreMetaSourceInformation(appProperties.getStore_meta_source_information());

storageSettings(appProperties, jpaStorageSettings);
jpaStorageSettings.setAllowContainsSearches(appProperties.getAllow_contains_searches());
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
jpaStorageSettings.setDefaultSearchParamsCanBeOverridden(
appProperties.getAllow_override_default_search_params());

jpaStorageSettings.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());

jpaStorageSettings.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
jpaStorageSettings.setIndexIdentifierOfType(appProperties.getEnable_index_of_type());
return jpaStorageSettings;
}

Expand Down Expand Up @@ -332,19 +339,6 @@ public HibernatePropertiesProvider jpaStarterDialectProvider(
return new JpaHibernatePropertiesProvider(myEntityManagerFactory);
}

protected StorageSettings storageSettings(AppProperties appProperties, JpaStorageSettings jpaStorageSettings) {
jpaStorageSettings.setAllowContainsSearches(appProperties.getAllow_contains_searches());
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
jpaStorageSettings.setDefaultSearchParamsCanBeOverridden(
appProperties.getAllow_override_default_search_params());

jpaStorageSettings.setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level());

jpaStorageSettings.setIndexOnContainedResources(appProperties.getEnable_index_contained_resource());
jpaStorageSettings.setIndexIdentifierOfType(appProperties.getEnable_index_of_type());
return jpaStorageSettings;
}

@Lazy
@Bean
public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@

@Configuration
@Conditional(OnDSTU3Condition.class)
@Import({JpaDstu3Config.class, StarterJpaConfig.class, StarterCrDstu3Config.class, ElasticsearchConfig.class})
@Import({JpaDstu3Config.class, StarterJpaConfig.class, StarterCrDstu3Config.class})
public class FhirServerConfigDstu3 {}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,5 @@

@Configuration
@Conditional(OnR4Condition.class)
@Import({
JpaR4Config.class,
StarterJpaConfig.class,
StarterCrR4Config.class,
ElasticsearchConfig.class,
StarterIpsConfig.class
})
@Import({JpaR4Config.class, StarterJpaConfig.class, StarterCrR4Config.class, StarterIpsConfig.class})
public class FhirServerConfigR4 {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@

@Configuration
@Conditional(OnR4BCondition.class)
@Import({JpaR4BConfig.class, SubscriptionTopicConfig.class, StarterJpaConfig.class, ElasticsearchConfig.class})
@Import({JpaR4BConfig.class, SubscriptionTopicConfig.class, StarterJpaConfig.class})
public class FhirServerConfigR4B {}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@

@Configuration
@Conditional(OnR5Condition.class)
@Import({StarterJpaConfig.class, JpaR5Config.class, SubscriptionTopicConfig.class, ElasticsearchConfig.class})
@Import({StarterJpaConfig.class, JpaR5Config.class, SubscriptionTopicConfig.class})
public class FhirServerConfigR5 {}
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
package ca.uhn.fhir.jpa.starter.common;

import ca.uhn.fhir.jpa.starter.AppProperties;
import org.springframework.boot.context.properties.bind.Binder;
import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class OnPartitionModeEnabled implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
var appProperties = Binder.get(context.getEnvironment())
.bind("hapi.fhir", AppProperties.class)
.orElse(null);
var appProperties = EnvironmentHelper.getConfiguration(context, "hapi.fhir", AppProperties.class);
if (appProperties == null) return false;
return appProperties.getPartitioning() != null;
}
Expand Down
Loading
Loading