Merge remote-tracking branch 'remotes/origin/master' into im_20200728_term_multi_version_support
This commit is contained in:
commit
2126c5c556
|
@ -32,7 +32,7 @@ import org.thymeleaf.util.Validate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class can be used to build a Bundle resource to be used as a FHIR transaction.
|
* This class can be used to build a Bundle resource to be used as a FHIR transaction.
|
||||||
*
|
* <p>
|
||||||
* This is not yet complete, and doesn't support all FHIR features. <b>USE WITH CAUTION</b> as the API
|
* This is not yet complete, and doesn't support all FHIR features. <b>USE WITH CAUTION</b> as the API
|
||||||
* may change.
|
* may change.
|
||||||
*
|
*
|
||||||
|
@ -52,6 +52,7 @@ public class TransactionBuilder {
|
||||||
private final BaseRuntimeChildDefinition myEntryRequestUrlChild;
|
private final BaseRuntimeChildDefinition myEntryRequestUrlChild;
|
||||||
private final BaseRuntimeChildDefinition myEntryRequestMethodChild;
|
private final BaseRuntimeChildDefinition myEntryRequestMethodChild;
|
||||||
private final BaseRuntimeElementDefinition<?> myEntryRequestMethodDef;
|
private final BaseRuntimeElementDefinition<?> myEntryRequestMethodDef;
|
||||||
|
private final BaseRuntimeChildDefinition myEntryRequestIfNoneExistChild;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -81,6 +82,7 @@ public class TransactionBuilder {
|
||||||
myEntryRequestMethodChild = myEntryRequestDef.getChildByName("method");
|
myEntryRequestMethodChild = myEntryRequestDef.getChildByName("method");
|
||||||
myEntryRequestMethodDef = myEntryRequestMethodChild.getChildByName("method");
|
myEntryRequestMethodDef = myEntryRequestMethodChild.getChildByName("method");
|
||||||
|
|
||||||
|
myEntryRequestIfNoneExistChild = myEntryRequestDef.getChildByName("ifNoneExist");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +91,47 @@ public class TransactionBuilder {
|
||||||
*
|
*
|
||||||
* @param theResource The resource to update
|
* @param theResource The resource to update
|
||||||
*/
|
*/
|
||||||
public TransactionBuilder addUpdateEntry(IBaseResource theResource) {
|
public UpdateBuilder addUpdateEntry(IBaseResource theResource) {
|
||||||
|
IBase request = addEntryAndReturnRequest(theResource);
|
||||||
|
|
||||||
|
// Bundle.entry.request.url
|
||||||
|
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||||
|
url.setValueAsString(theResource.getIdElement().toUnqualifiedVersionless().getValue());
|
||||||
|
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
||||||
|
|
||||||
|
// Bundle.entry.request.url
|
||||||
|
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
||||||
|
method.setValueAsString("PUT");
|
||||||
|
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
||||||
|
|
||||||
|
return new UpdateBuilder(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an entry containing an create (POST) request
|
||||||
|
*
|
||||||
|
* @param theResource The resource to create
|
||||||
|
*/
|
||||||
|
public CreateBuilder addCreateEntry(IBaseResource theResource) {
|
||||||
|
IBase request = addEntryAndReturnRequest(theResource);
|
||||||
|
|
||||||
|
String resourceType = myContext.getResourceType(theResource);
|
||||||
|
|
||||||
|
// Bundle.entry.request.url
|
||||||
|
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||||
|
url.setValueAsString(resourceType);
|
||||||
|
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
||||||
|
|
||||||
|
// Bundle.entry.request.url
|
||||||
|
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
||||||
|
method.setValueAsString("POST");
|
||||||
|
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
||||||
|
|
||||||
|
return new CreateBuilder(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBase addEntryAndReturnRequest(IBaseResource theResource) {
|
||||||
Validate.notNull(theResource, "theResource must not be null");
|
Validate.notNull(theResource, "theResource must not be null");
|
||||||
Validate.notEmpty(theResource.getIdElement().getValue(), "theResource must have an ID");
|
|
||||||
|
|
||||||
IBase entry = myEntryDef.newInstance();
|
IBase entry = myEntryDef.newInstance();
|
||||||
myEntryChild.getMutator().addValue(myBundle, entry);
|
myEntryChild.getMutator().addValue(myBundle, entry);
|
||||||
|
@ -107,24 +147,47 @@ public class TransactionBuilder {
|
||||||
// Bundle.entry.request
|
// Bundle.entry.request
|
||||||
IBase request = myEntryRequestDef.newInstance();
|
IBase request = myEntryRequestDef.newInstance();
|
||||||
myEntryRequestChild.getMutator().setValue(entry, request);
|
myEntryRequestChild.getMutator().setValue(entry, request);
|
||||||
|
return request;
|
||||||
// Bundle.entry.request.url
|
|
||||||
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
|
||||||
url.setValueAsString(theResource.getIdElement().toUnqualifiedVersionless().getValue());
|
|
||||||
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
|
||||||
|
|
||||||
// Bundle.entry.request.url
|
|
||||||
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
|
||||||
method.setValueAsString("PUT");
|
|
||||||
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public IBaseBundle getBundle() {
|
public IBaseBundle getBundle() {
|
||||||
return myBundle;
|
return myBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class UpdateBuilder {
|
||||||
|
|
||||||
|
private final IPrimitiveType<?> myUrl;
|
||||||
|
|
||||||
|
public UpdateBuilder(IPrimitiveType<?> theUrl) {
|
||||||
|
myUrl = theUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make this update a Conditional Update
|
||||||
|
*/
|
||||||
|
public void conditional(String theConditionalUrl) {
|
||||||
|
myUrl.setValueAsString(theConditionalUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateBuilder {
|
||||||
|
private final IBase myRequest;
|
||||||
|
|
||||||
|
public CreateBuilder(IBase theRequest) {
|
||||||
|
myRequest = theRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make this create a Conditional Create
|
||||||
|
*/
|
||||||
|
public void conditional(String theConditionalUrl) {
|
||||||
|
IPrimitiveType<?> ifNoneExist = (IPrimitiveType<?>) myContext.getElementDefinition("string").newInstance();
|
||||||
|
ifNoneExist.setValueAsString(theConditionalUrl);
|
||||||
|
|
||||||
|
myEntryRequestIfNoneExistChild.getMutator().setValue(myRequest, ifNoneExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package ca.uhn.hapi.fhir.docs;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Docs
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2020 University Health Network
|
||||||
|
* %%
|
||||||
|
* 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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
import ca.uhn.fhir.util.TransactionBuilder;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class TransactionBuilderExamples {
|
||||||
|
|
||||||
|
private FhirContext myFhirContext;
|
||||||
|
private IGenericClient myFhirClient;
|
||||||
|
|
||||||
|
public void update() throws FHIRException {
|
||||||
|
//START SNIPPET: update
|
||||||
|
// Create a TransactionBuilder
|
||||||
|
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
|
||||||
|
|
||||||
|
// Create a Patient to update
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("http://foo/Patient/123");
|
||||||
|
patient.setActive(true);
|
||||||
|
|
||||||
|
// Add the patient as an update (aka PUT) to the Bundle
|
||||||
|
builder.addUpdateEntry(patient);
|
||||||
|
|
||||||
|
// Execute the transaction
|
||||||
|
IBaseBundle outcome = myFhirClient.transaction().withBundle(builder.getBundle()).execute();
|
||||||
|
//END SNIPPET: update
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateConditional() throws FHIRException {
|
||||||
|
//START SNIPPET: updateConditional
|
||||||
|
// Create a TransactionBuilder
|
||||||
|
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
|
||||||
|
|
||||||
|
// Create a Patient to update
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
patient.addIdentifier().setSystem("http://foo").setValue("bar");
|
||||||
|
|
||||||
|
// Add the patient as an update (aka PUT) to the Bundle
|
||||||
|
builder.addUpdateEntry(patient).conditional("Patient?identifier=http://foo|bar");
|
||||||
|
|
||||||
|
// Execute the transaction
|
||||||
|
IBaseBundle outcome = myFhirClient.transaction().withBundle(builder.getBundle()).execute();
|
||||||
|
//END SNIPPET: updateConditional
|
||||||
|
}
|
||||||
|
|
||||||
|
public void create() throws FHIRException {
|
||||||
|
//START SNIPPET: create
|
||||||
|
// Create a TransactionBuilder
|
||||||
|
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
|
||||||
|
|
||||||
|
// Create a Patient to create
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
|
||||||
|
// Add the patient as a create (aka POST) to the Bundle
|
||||||
|
builder.addCreateEntry(patient);
|
||||||
|
|
||||||
|
// Execute the transaction
|
||||||
|
IBaseBundle outcome = myFhirClient.transaction().withBundle(builder.getBundle()).execute();
|
||||||
|
//END SNIPPET: create
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createConditional() throws FHIRException {
|
||||||
|
//START SNIPPET: createConditional
|
||||||
|
// Create a TransactionBuilder
|
||||||
|
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
|
||||||
|
|
||||||
|
// Create a Patient to create
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
patient.addIdentifier().setSystem("http://foo").setValue("bar");
|
||||||
|
|
||||||
|
// Add the patient as a create (aka POST) to the Bundle
|
||||||
|
builder.addCreateEntry(patient).conditional("Patient?identifier=http://foo|bar");
|
||||||
|
|
||||||
|
// Execute the transaction
|
||||||
|
IBaseBundle outcome = myFhirClient.transaction().withBundle(builder.getBundle()).execute();
|
||||||
|
//END SNIPPET: createConditional
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 2041
|
||||||
|
title: "A new class called TransactionBuilder has been added. This class can be used to build FHIR Transaction Bundles easily."
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 2043
|
||||||
|
title: "The JPA SearchParameetr validator did not reject custom Search Parameetrs with an invalid path
|
||||||
|
expression consisting of only a resource type and no element name. Creating such a search parameter could cause
|
||||||
|
strange indexing issues, so this has been added to the validator."
|
|
@ -15,6 +15,7 @@ page.model.profiles_and_extensions=Profiles and Extensions
|
||||||
page.model.converter=Version Converters
|
page.model.converter=Version Converters
|
||||||
page.model.custom_structures=Custom Structures
|
page.model.custom_structures=Custom Structures
|
||||||
page.model.narrative_generation=Narrative Generation
|
page.model.narrative_generation=Narrative Generation
|
||||||
|
page.model.transaction_builder=Transaction Builder
|
||||||
|
|
||||||
section.client.title=Client
|
section.client.title=Client
|
||||||
page.client.introduction=Introduction
|
page.client.introduction=Introduction
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Transaction Builder
|
||||||
|
|
||||||
|
The TransactionBuilder ([JavaDoc](/hapi-fhir/apidocs/hapi-fhir-base/ca/uhn/fhir/util/TransactionBuilder.html)) can be used to construct FHIR Transaction Bundles.
|
||||||
|
|
||||||
|
Note that this class is a work in progress! It does not yet support all transaction features. We will add more features over time, and document them here. Pull requests are welcomed.
|
||||||
|
|
||||||
|
# Resource Create
|
||||||
|
|
||||||
|
To add an update (aka PUT) operation to a transaction bundle
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/TransactionBuilderExamples.java|create}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conditional Create
|
||||||
|
|
||||||
|
If you want to perform a conditional create:
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/TransactionBuilderExamples.java|createConditional}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Resource Updates
|
||||||
|
|
||||||
|
To add an update (aka PUT) operation to a transaction bundle
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/TransactionBuilderExamples.java|update}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conditional Update
|
||||||
|
|
||||||
|
If you want to perform a conditional update:
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/TransactionBuilderExamples.java|updateConditional}}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,7 @@ import java.util.Date;
|
||||||
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.subscription.*"),
|
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.subscription.*"),
|
||||||
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.searchparam.*"),
|
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.searchparam.*"),
|
||||||
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.empi.*"),
|
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.empi.*"),
|
||||||
|
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.starter.*"),
|
||||||
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.batch.*")
|
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.batch.*")
|
||||||
})
|
})
|
||||||
@Import({
|
@Import({
|
||||||
|
|
|
@ -30,16 +30,22 @@ import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.SearchParamTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
|
import org.hl7.fhir.convertors.conv10_40.SearchParameter10_40;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterDstu2 extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
public class FhirResourceDaoSearchParameterDstu2 extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
private ISearchParamExtractor mySearchParamExtractor;
|
||||||
|
private FhirContext myDstu2Hl7OrgContext = FhirContext.forDstu2Hl7Org();
|
||||||
|
|
||||||
protected void markAffectedResources(SearchParameter theResource) {
|
protected void markAffectedResources(SearchParameter theResource) {
|
||||||
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
||||||
|
@ -70,17 +76,16 @@ public class FhirResourceDaoSearchParameterDstu2 extends BaseHapiFhirResourceDao
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
super.validateResourceForStorage(theResource, theEntityToSave);
|
||||||
|
|
||||||
Enum<?> status = theResource.getStatusElement().getValueAsEnum();
|
String encoded = getContext().newJsonParser().encodeResourceToString(theResource);
|
||||||
List<BoundCodeDt<ResourceTypeEnum>> base = Collections.emptyList();
|
org.hl7.fhir.dstu2.model.SearchParameter hl7Org = myDstu2Hl7OrgContext.newJsonParser().parseResource(org.hl7.fhir.dstu2.model.SearchParameter.class, encoded);
|
||||||
if (theResource.getBase() != null) {
|
|
||||||
base = Arrays.asList(theResource.getBaseElement());
|
org.hl7.fhir.r4.model.SearchParameter convertedSp = SearchParameter10_40.convertSearchParameter(hl7Org);
|
||||||
}
|
if (isBlank(convertedSp.getExpression()) && isNotBlank(hl7Org.getXpath())) {
|
||||||
String expression = theResource.getXpath();
|
convertedSp.setExpression(hl7Org.getXpath());
|
||||||
FhirContext context = getContext();
|
}
|
||||||
SearchParamTypeEnum type = theResource.getTypeElement().getValueAsEnum();
|
|
||||||
String code = theResource.getCode();
|
FhirResourceDaoSearchParameterR4.validateSearchParam(convertedSp, getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(mySearchParamRegistry, mySearchParamExtractor, code, type, status, base, expression, context, getConfig());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||||
|
import org.hl7.fhir.convertors.conv10_40.SearchParameter10_40;
|
||||||
|
import org.hl7.fhir.convertors.conv30_40.SearchParameter30_40;
|
||||||
import org.hl7.fhir.dstu3.model.CodeType;
|
import org.hl7.fhir.dstu3.model.CodeType;
|
||||||
import org.hl7.fhir.dstu3.model.Enumerations;
|
import org.hl7.fhir.dstu3.model.Enumerations;
|
||||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
import org.hl7.fhir.dstu3.model.SearchParameter;
|
||||||
|
@ -67,14 +69,7 @@ public class FhirResourceDaoSearchParameterDstu3 extends BaseHapiFhirResourceDao
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
super.validateResourceForStorage(theResource, theEntityToSave);
|
||||||
|
|
||||||
Enum<?> status = theResource.getStatus();
|
FhirResourceDaoSearchParameterR4.validateSearchParam(SearchParameter30_40.convertSearchParameter(theResource), getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
||||||
List<CodeType> base = theResource.getBase();
|
|
||||||
String expression = theResource.getExpression();
|
|
||||||
FhirContext context = getContext();
|
|
||||||
Enumerations.SearchParamType type = theResource.getType();
|
|
||||||
String code = theResource.getCode();
|
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(mySearchParamRegistry, mySearchParamExtractor, code, type, status, base, expression, context, getConfig());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,10 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
@ -21,6 +23,7 @@ import org.hl7.fhir.r4.model.SearchParameter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
|
@ -46,6 +49,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
||||||
|
|
||||||
|
private static final Pattern REGEX_SP_EXPRESSION_HAS_PATH = Pattern.compile("[( ]*([A-Z][a-zA-Z]+\\.)?[a-z].*");
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
private ISearchParamExtractor mySearchParamExtractor;
|
||||||
|
|
||||||
|
@ -78,27 +82,21 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
super.validateResourceForStorage(theResource, theEntityToSave);
|
||||||
|
|
||||||
Enum<?> status = theResource.getStatus();
|
validateSearchParam(theResource, getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
||||||
List<CodeType> base = theResource.getBase();
|
|
||||||
String code = theResource.getCode();
|
|
||||||
String expression = theResource.getExpression();
|
|
||||||
FhirContext context = getContext();
|
|
||||||
Enum<?> type = theResource.getType();
|
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(mySearchParamRegistry, mySearchParamExtractor, code, type, status, base, expression, context, getConfig());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void validateSearchParam(ISearchParamRegistry theSearchParamRegistry, ISearchParamExtractor theSearchParamExtractor, String theCode, Enum<?> theType, Enum<?> theStatus, List<? extends IPrimitiveType> theBase, String theExpression, FhirContext theContext, DaoConfig theDaoConfig) {
|
public static void validateSearchParam(SearchParameter theResource, FhirContext theContext, DaoConfig theDaoConfig, ISearchParamRegistry theSearchParamRegistry, ISearchParamExtractor theSearchParamExtractor) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If overriding built-in SPs is disabled on this server, make sure we aren't
|
* If overriding built-in SPs is disabled on this server, make sure we aren't
|
||||||
* doing that
|
* doing that
|
||||||
*/
|
*/
|
||||||
if (theDaoConfig.getModelConfig().isDefaultSearchParamsCanBeOverridden() == false) {
|
if (theDaoConfig.getModelConfig().isDefaultSearchParamsCanBeOverridden() == false) {
|
||||||
for (IPrimitiveType<?> nextBaseType : theBase) {
|
for (IPrimitiveType<?> nextBaseType : theResource.getBase()) {
|
||||||
String nextBase = nextBaseType.getValueAsString();
|
String nextBase = nextBaseType.getValueAsString();
|
||||||
RuntimeSearchParam existingSearchParam = theSearchParamRegistry.getActiveSearchParam(nextBase, theCode);
|
RuntimeSearchParam existingSearchParam = theSearchParamRegistry.getActiveSearchParam(nextBase, theResource.getCode());
|
||||||
if (existingSearchParam != null && existingSearchParam.getId() == null) {
|
if (existingSearchParam != null && existingSearchParam.getId() == null) {
|
||||||
throw new UnprocessableEntityException("Can not override built-in search parameter " + nextBase + ":" + theCode + " because overriding is disabled on this server");
|
throw new UnprocessableEntityException("Can not override built-in search parameter " + nextBase + ":" + theResource.getCode() + " because overriding is disabled on this server");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,37 +105,52 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
* Everything below is validating that the SP is actually valid. We'll only do that if the
|
* Everything below is validating that the SP is actually valid. We'll only do that if the
|
||||||
* SPO is active, so that we don't block people from uploading works-in-progress
|
* SPO is active, so that we don't block people from uploading works-in-progress
|
||||||
*/
|
*/
|
||||||
if (theStatus == null) {
|
if (theResource.getStatus() == null) {
|
||||||
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid");
|
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid");
|
||||||
}
|
}
|
||||||
if (!theStatus.name().equals("ACTIVE")) {
|
if (!theResource.getStatus().name().equals("ACTIVE")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ElementUtil.isEmpty(theBase) && (theType == null || !Enumerations.SearchParamType.COMPOSITE.name().equals(theType.name()))) {
|
if (ElementUtil.isEmpty(theResource.getBase()) && (theResource.getType() == null || !Enumerations.SearchParamType.COMPOSITE.name().equals(theResource.getType().name()))) {
|
||||||
throw new UnprocessableEntityException("SearchParameter.base is missing");
|
throw new UnprocessableEntityException("SearchParameter.base is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theType != null && theType.name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(theExpression)) {
|
boolean isUnique = theResource.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE).stream().anyMatch(t-> "true".equals(t.getValueAsPrimitive().getValueAsString()));
|
||||||
|
|
||||||
|
if (theResource.getType() != null && theResource.getType().name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(theResource.getExpression())) {
|
||||||
|
|
||||||
// this is ok
|
// this is ok
|
||||||
|
|
||||||
} else if (isBlank(theExpression)) {
|
} else if (isBlank(theResource.getExpression())) {
|
||||||
|
|
||||||
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
throw new UnprocessableEntityException("SearchParameter.expression is missing");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
theExpression = theExpression.trim();
|
String expression = theResource.getExpression().trim();
|
||||||
|
|
||||||
|
if (isUnique) {
|
||||||
|
if (theResource.getComponent().size() == 0) {
|
||||||
|
throw new UnprocessableEntityException("SearchParameter is marked as unique but has no components");
|
||||||
|
}
|
||||||
|
for (SearchParameter.SearchParameterComponentComponent next : theResource.getComponent()) {
|
||||||
|
if (isBlank(next.getDefinition())) {
|
||||||
|
throw new UnprocessableEntityException("SearchParameter is marked as unique but is missing component.definition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
if (!theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
||||||
String[] expressionSplit = theSearchParamExtractor.split(theExpression);
|
|
||||||
|
// DSTU3 and below
|
||||||
|
String[] expressionSplit = theSearchParamExtractor.split(expression);
|
||||||
for (String nextPath : expressionSplit) {
|
for (String nextPath : expressionSplit) {
|
||||||
nextPath = nextPath.trim();
|
nextPath = nextPath.trim();
|
||||||
|
|
||||||
int dotIdx = nextPath.indexOf('.');
|
int dotIdx = nextPath.indexOf('.');
|
||||||
if (dotIdx == -1) {
|
if (dotIdx == -1) {
|
||||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
|
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourceName = nextPath.substring(0, dotIdx);
|
String resourceName = nextPath.substring(0, dotIdx);
|
||||||
|
@ -162,10 +175,15 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
if (!isUnique && theResource.getType() != Enumerations.SearchParamType.COMPOSITE && theResource.getType() != Enumerations.SearchParamType.SPECIAL && !REGEX_SP_EXPRESSION_HAS_PATH.matcher(expression).matches()) {
|
||||||
|
throw new UnprocessableEntityException("SearchParameter.expression value \"" + expression + "\" is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
// R4 and above
|
||||||
try {
|
try {
|
||||||
theContext.newFluentPath().parse(theExpression);
|
theContext.newFluentPath().parse(expression);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + theExpression + "\": " + e.getMessage());
|
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + expression + "\": " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||||
|
import org.hl7.fhir.convertors.conv30_40.SearchParameter30_40;
|
||||||
|
import org.hl7.fhir.convertors.conv40_50.SearchParameter40_50;
|
||||||
import org.hl7.fhir.r5.model.CodeType;
|
import org.hl7.fhir.r5.model.CodeType;
|
||||||
import org.hl7.fhir.r5.model.SearchParameter;
|
import org.hl7.fhir.r5.model.SearchParameter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -66,14 +68,7 @@ public class FhirResourceDaoSearchParameterR5 extends BaseHapiFhirResourceDao<Se
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
super.validateResourceForStorage(theResource, theEntityToSave);
|
||||||
|
|
||||||
Enum<?> status = theResource.getStatus();
|
FhirResourceDaoSearchParameterR4.validateSearchParam(SearchParameter40_50.convertSearchParameter(theResource), getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
||||||
List<CodeType> base = theResource.getBase();
|
|
||||||
String code = theResource.getCode();
|
|
||||||
String expression = theResource.getExpression();
|
|
||||||
FhirContext context = getContext();
|
|
||||||
Enum<?> type = theResource.getType();
|
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(mySearchParamRegistry, mySearchParamExtractor, code, type, status, base, expression, context, getConfig());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
mySearchParameterDao.create(fooSp, mySrd);
|
mySearchParameterDao.create(fooSp, mySrd);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name", e.getMessage());
|
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name.", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
mySearchParameterDao.create(fooSp, mySrd);
|
mySearchParameterDao.create(fooSp, mySrd);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name", e.getMessage());
|
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name.", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -288,7 +288,7 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
||||||
mySearchParameterDao.create(fooSp, mySrd);
|
mySearchParameterDao.create(fooSp, mySrd);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name", e.getMessage());
|
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name.", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1316,7 +1316,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
params.setLoadSynchronous(true);
|
params.setLoadSynchronous(true);
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
List<IBaseResource> patients = toList(myPatientDao.search(params));
|
List<IBaseResource> patients = toList(myPatientDao.search(params));
|
||||||
|
myCaptureQueriesListener.logAllQueriesForCurrentThread();
|
||||||
assertTrue(patients.size() >= 2);
|
assertTrue(patients.size() >= 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
@ -230,7 +231,7 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
sp = new SearchParameter();
|
sp = new SearchParameter();
|
||||||
sp.setId("SearchParameter/patient-uniq-identifier");
|
sp.setId("SearchParameter/patient-uniq-identifier");
|
||||||
sp.setCode("patient-uniq-identifier");
|
sp.setCode("patient-uniq-identifier");
|
||||||
sp.setExpression("Patient.identifier");
|
sp.setExpression("Patient");
|
||||||
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||||
sp.setStatus(PublicationStatus.ACTIVE);
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
sp.addBase("Patient");
|
sp.addBase("Patient");
|
||||||
|
@ -362,6 +363,50 @@ public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test {
|
||||||
mySearchParamRegistry.forceRefresh();
|
mySearchParamRegistry.forceRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateUniqueSpWithNoComponent() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-uniq-identifier");
|
||||||
|
sp.setCode("patient-uniq-identifier");
|
||||||
|
sp.setExpression("Patient");
|
||||||
|
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
sp.addExtension()
|
||||||
|
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
|
||||||
|
.setValue(new BooleanType(true));
|
||||||
|
|
||||||
|
try {
|
||||||
|
mySearchParameterDao.create(sp);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertEquals("SearchParameter is marked as unique but has no components", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateUniqueSpWithNoComponentDefinition() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setId("SearchParameter/patient-uniq-identifier");
|
||||||
|
sp.setCode("patient-uniq-identifier");
|
||||||
|
sp.setExpression("Patient");
|
||||||
|
sp.setType(Enumerations.SearchParamType.COMPOSITE);
|
||||||
|
sp.setStatus(PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("Patient");
|
||||||
|
sp.addComponent() .setExpression("Patient");
|
||||||
|
sp.addExtension()
|
||||||
|
.setUrl(HapiExtensions.EXT_SP_UNIQUE)
|
||||||
|
.setValue(new BooleanType(true));
|
||||||
|
|
||||||
|
try {
|
||||||
|
mySearchParameterDao.create(sp);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertEquals("SearchParameter is marked as unique but is missing component.definition", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoubleMatchingOnAnd_Search() {
|
public void testDoubleMatchingOnAnd_Search() {
|
||||||
createUniqueIndexPatientIdentifier();
|
createUniqueIndexPatientIdentifier();
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class FhirResourceDaoSearchParameterR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testValidateInvalidExpression() {
|
public void testValidateInvalidExpression() {
|
||||||
SearchParameter nextSearchParameter = new SearchParameter();
|
SearchParameter nextSearchParameter = new SearchParameter();
|
||||||
nextSearchParameter.setExpression("Patient////");
|
nextSearchParameter.setExpression("Patient.ex////");
|
||||||
nextSearchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
nextSearchParameter.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
nextSearchParameter.setType(Enumerations.SearchParamType.STRING);
|
nextSearchParameter.setType(Enumerations.SearchParamType.STRING);
|
||||||
nextSearchParameter.addBase("Patient");
|
nextSearchParameter.addBase("Patient");
|
||||||
|
@ -84,7 +84,7 @@ public class FhirResourceDaoSearchParameterR4Test {
|
||||||
myDao.validateResourceForStorage(nextSearchParameter, null);
|
myDao.validateResourceForStorage(nextSearchParameter, null);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals("Invalid SearchParameter.expression value \"Patient////\": Error in ?? at 1, 1: Premature ExpressionNode termination at unexpected token \"////\"", e.getMessage());
|
assertEquals("Invalid SearchParameter.expression value \"Patient.ex////\": Error in ?? at 1, 1: Premature ExpressionNode termination at unexpected token \"////\"", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponen
|
||||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
|
||||||
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
|
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||||
import org.hl7.fhir.r4.model.Observation;
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
||||||
|
@ -118,6 +119,23 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void saveCreateSearchParamInvalidWithPathMissingElementName() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setCode("foo");
|
||||||
|
sp.setExpression("Patient");
|
||||||
|
sp.addBase("Patient");
|
||||||
|
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
sp.setTitle("Foo Param");
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.create().resource(sp).execute();
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertEquals("HTTP 422 Unprocessable Entity: SearchParameter.expression value \"Patient\" is invalid", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConformanceOverrideAllowed() {
|
public void testConformanceOverrideAllowed() {
|
||||||
myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
|
myModelConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhirtest.config;
|
package ca.uhn.fhirtest.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
|
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
|
||||||
|
@ -76,6 +77,13 @@ public class CommonConfig {
|
||||||
return new PartitionSettings();
|
return new PartitionSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SqlCaptureInterceptor sqlCaptureInterceptor(IInterceptorService theInterceptorService) {
|
||||||
|
SqlCaptureInterceptor retVal = new SqlCaptureInterceptor();
|
||||||
|
theInterceptorService.registerInterceptor(retVal);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isLocalTestMode() {
|
public static boolean isLocalTestMode() {
|
||||||
return "true".equalsIgnoreCase(System.getProperty("testmode.local"));
|
return "true".equalsIgnoreCase(System.getProperty("testmode.local"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package ca.uhn.fhirtest.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.api.Hook;
|
||||||
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
|
import ca.uhn.fhir.jpa.util.SqlQuery;
|
||||||
|
import ca.uhn.fhir.jpa.util.SqlQueryList;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At some point we should move this to somewhere accessible to other projects,
|
||||||
|
* and document it.
|
||||||
|
*/
|
||||||
|
public class SqlCaptureInterceptor {
|
||||||
|
|
||||||
|
@Hook(Pointcut.JPA_PERFTRACE_RAW_SQL)
|
||||||
|
public void captureSql(ServletRequestDetails theRequestDetails, SqlQueryList theQueries) {
|
||||||
|
if (theRequestDetails != null) {
|
||||||
|
String[] captureSqls = theRequestDetails.getParameters().get("_captureSql");
|
||||||
|
if (captureSqls == null || !captureSqls[0].equals("true")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < theQueries.size(); i++) {
|
||||||
|
SqlQuery nextQuery = theQueries.get(i);
|
||||||
|
String sql = nextQuery.getSql(true, false);
|
||||||
|
sql = UrlUtil.sanitizeUrlPart(sql);
|
||||||
|
|
||||||
|
theRequestDetails.getResponse().addHeader("X-Executed-SQL", sql);
|
||||||
|
theRequestDetails.getResponse().addHeader("X-Executed-SQL-Outcome", "Returned " + nextQuery.getSize() + " in " + StopWatch.formatMillis(nextQuery.getElapsedTime()));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,11 +4,13 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
|
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||||
|
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||||
import ca.uhn.fhir.validation.IValidatorModule;
|
import ca.uhn.fhir.validation.IValidatorModule;
|
||||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
||||||
|
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hibernate.dialect.PostgreSQL94Dialect;
|
import org.hibernate.dialect.PostgreSQL94Dialect;
|
||||||
|
@ -27,6 +29,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
import javax.persistence.EntityManagerFactory;
|
import javax.persistence.EntityManagerFactory;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(CommonConfig.class)
|
@Import(CommonConfig.class)
|
||||||
|
@ -91,7 +94,16 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
|
||||||
retVal.setPassword(myDbPassword);
|
retVal.setPassword(myDbPassword);
|
||||||
retVal.setDefaultQueryTimeout(20);
|
retVal.setDefaultQueryTimeout(20);
|
||||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||||
return retVal;
|
|
||||||
|
DataSource dataSource = ProxyDataSourceBuilder
|
||||||
|
.create(retVal)
|
||||||
|
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||||
|
.logSlowQueryBySlf4j(10000, TimeUnit.MILLISECONDS)
|
||||||
|
.afterQuery(new CurrentThreadCaptureQueriesListener())
|
||||||
|
.countQuery()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Primary
|
@Primary
|
||||||
|
|
|
@ -5,10 +5,12 @@ import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||||
|
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
||||||
|
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hibernate.dialect.PostgreSQL94Dialect;
|
import org.hibernate.dialect.PostgreSQL94Dialect;
|
||||||
|
@ -28,6 +30,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
import javax.persistence.EntityManagerFactory;
|
import javax.persistence.EntityManagerFactory;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(CommonConfig.class)
|
@Import(CommonConfig.class)
|
||||||
|
@ -105,7 +108,16 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
||||||
retVal.setPassword(myDbPassword);
|
retVal.setPassword(myDbPassword);
|
||||||
retVal.setDefaultQueryTimeout(20);
|
retVal.setDefaultQueryTimeout(20);
|
||||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||||
return retVal;
|
|
||||||
|
DataSource dataSource = ProxyDataSourceBuilder
|
||||||
|
.create(retVal)
|
||||||
|
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||||
|
.logSlowQueryBySlf4j(10000, TimeUnit.MILLISECONDS)
|
||||||
|
.afterQuery(new CurrentThreadCaptureQueriesListener())
|
||||||
|
.countQuery()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,10 +5,13 @@ import ca.uhn.fhir.jpa.config.BaseJavaConfigR4;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||||
|
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.ThreadLocalCapturingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
||||||
|
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hibernate.dialect.PostgreSQL94Dialect;
|
import org.hibernate.dialect.PostgreSQL94Dialect;
|
||||||
|
@ -28,6 +31,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
import javax.persistence.EntityManagerFactory;
|
import javax.persistence.EntityManagerFactory;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(CommonConfig.class)
|
@Import(CommonConfig.class)
|
||||||
|
@ -90,7 +94,16 @@ public class TestR4Config extends BaseJavaConfigR4 {
|
||||||
retVal.setPassword(myDbPassword);
|
retVal.setPassword(myDbPassword);
|
||||||
retVal.setDefaultQueryTimeout(20);
|
retVal.setDefaultQueryTimeout(20);
|
||||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||||
return retVal;
|
|
||||||
|
DataSource dataSource = ProxyDataSourceBuilder
|
||||||
|
.create(retVal)
|
||||||
|
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||||
|
.logSlowQueryBySlf4j(10000, TimeUnit.MILLISECONDS)
|
||||||
|
.afterQuery(new CurrentThreadCaptureQueriesListener())
|
||||||
|
.countQuery()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,10 +5,12 @@ import ca.uhn.fhir.jpa.config.BaseJavaConfigR5;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||||
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
|
||||||
|
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
||||||
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor;
|
||||||
|
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||||
import org.apache.commons.dbcp2.BasicDataSource;
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
import org.hibernate.dialect.PostgreSQL94Dialect;
|
import org.hibernate.dialect.PostgreSQL94Dialect;
|
||||||
|
@ -28,6 +30,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
import javax.persistence.EntityManagerFactory;
|
import javax.persistence.EntityManagerFactory;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(CommonConfig.class)
|
@Import(CommonConfig.class)
|
||||||
|
@ -90,7 +93,16 @@ public class TestR5Config extends BaseJavaConfigR5 {
|
||||||
retVal.setPassword(myDbPassword);
|
retVal.setPassword(myDbPassword);
|
||||||
retVal.setDefaultQueryTimeout(20);
|
retVal.setDefaultQueryTimeout(20);
|
||||||
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
retVal.setMaxConnLifetimeMillis(5 * DateUtils.MILLIS_PER_MINUTE);
|
||||||
return retVal;
|
|
||||||
|
DataSource dataSource = ProxyDataSourceBuilder
|
||||||
|
.create(retVal)
|
||||||
|
// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||||
|
.logSlowQueryBySlf4j(10000, TimeUnit.MILLISECONDS)
|
||||||
|
.afterQuery(new CurrentThreadCaptureQueriesListener())
|
||||||
|
.countQuery()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -37,4 +37,73 @@ public class TransactionBuilderTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddEntryUpdateConditional() {
|
||||||
|
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("http://foo/Patient/123");
|
||||||
|
patient.setActive(true);
|
||||||
|
builder.addUpdateEntry(patient).conditional("Patient?active=true");
|
||||||
|
|
||||||
|
Bundle bundle = (Bundle) builder.getBundle();
|
||||||
|
ourLog.info("Bundle:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||||
|
|
||||||
|
assertEquals(Bundle.BundleType.TRANSACTION, bundle.getType());
|
||||||
|
assertEquals(1, bundle.getEntry().size());
|
||||||
|
assertSame(patient, bundle.getEntry().get(0).getResource());
|
||||||
|
assertEquals("http://foo/Patient/123", bundle.getEntry().get(0).getFullUrl());
|
||||||
|
assertEquals("Patient?active=true", bundle.getEntry().get(0).getRequest().getUrl());
|
||||||
|
assertEquals(Bundle.HTTPVerb.PUT, bundle.getEntry().get(0).getRequest().getMethod());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddEntryCreate() {
|
||||||
|
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
builder.addCreateEntry(patient);
|
||||||
|
|
||||||
|
Bundle bundle = (Bundle) builder.getBundle();
|
||||||
|
ourLog.info("Bundle:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||||
|
|
||||||
|
assertEquals(Bundle.BundleType.TRANSACTION, bundle.getType());
|
||||||
|
assertEquals(1, bundle.getEntry().size());
|
||||||
|
assertSame(patient, bundle.getEntry().get(0).getResource());
|
||||||
|
assertEquals(null, bundle.getEntry().get(0).getFullUrl());
|
||||||
|
assertEquals("Patient", bundle.getEntry().get(0).getRequest().getUrl());
|
||||||
|
assertEquals(Bundle.HTTPVerb.POST, bundle.getEntry().get(0).getRequest().getMethod());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddEntryCreateConditional() {
|
||||||
|
TransactionBuilder builder = new TransactionBuilder(myFhirContext);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
builder.addCreateEntry(patient).conditional("Patient?active=true");
|
||||||
|
|
||||||
|
Bundle bundle = (Bundle) builder.getBundle();
|
||||||
|
ourLog.info("Bundle:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||||
|
|
||||||
|
assertEquals(Bundle.BundleType.TRANSACTION, bundle.getType());
|
||||||
|
assertEquals(1, bundle.getEntry().size());
|
||||||
|
assertSame(patient, bundle.getEntry().get(0).getResource());
|
||||||
|
assertEquals(null, bundle.getEntry().get(0).getFullUrl());
|
||||||
|
assertEquals("Patient", bundle.getEntry().get(0).getRequest().getUrl());
|
||||||
|
assertEquals("Patient?active=true", bundle.getEntry().get(0).getRequest().getIfNoneExist());
|
||||||
|
assertEquals(Bundle.HTTPVerb.POST, bundle.getEntry().get(0).getRequest().getMethod());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue