merge master
This commit is contained in:
commit
d70be51003
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -22,10 +22,13 @@ package ca.uhn.fhir.context.support;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
@ -321,6 +324,19 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
||||||
} else {
|
} else {
|
||||||
ourLog.warn("Unable to load resource: {}", theClasspath);
|
ourLog.warn("Unable to load resource: {}", theClasspath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load built-in system
|
||||||
|
|
||||||
|
if (myCtx.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||||
|
String storageCodeEnum = ClasspathUtil.loadResource("org/hl7/fhir/common/hapi/validation/support/HapiFhirStorageResponseCode.json");
|
||||||
|
IBaseResource storageCodeCodeSystem = myCtx.newJsonParser().setParserErrorHandler(new LenientErrorHandler()).parseResource(storageCodeEnum);
|
||||||
|
String url = myCtx.newTerser().getSinglePrimitiveValueOrNull(storageCodeCodeSystem, "url");
|
||||||
|
theCodeSystems.put(url, storageCodeCodeSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadStructureDefinitions(Map<String, IBaseResource> theCodeSystems, String theClasspath) {
|
private void loadStructureDefinitions(Map<String, IBaseResource> theCodeSystems, String theClasspath) {
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package ca.uhn.fhir.model.api;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2022 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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface ICodingEnum {
|
||||||
|
|
||||||
|
String getCode();
|
||||||
|
String getSystem();
|
||||||
|
String getDisplay();
|
||||||
|
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -64,6 +65,23 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
*/
|
*/
|
||||||
public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
||||||
|
|
||||||
|
// TODO: JA - Replace all of the various other get/put methods in subclasses with just using the two that are here
|
||||||
|
public T get(IBaseResource theResource) {
|
||||||
|
if (theResource instanceof IAnyResource) {
|
||||||
|
return (T) theResource.getUserData(name());
|
||||||
|
} else {
|
||||||
|
return (T) ((IResource)theResource).getResourceMetadata().get(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void put(IBaseResource theResource, T theValue) {
|
||||||
|
if (theResource instanceof IAnyResource) {
|
||||||
|
theResource.setUserData(name(), theValue);
|
||||||
|
} else {
|
||||||
|
((IResource)theResource).getResourceMetadata().put(this, theValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If present and populated with a date/time (as an instance of {@link InstantDt}), this value is an indication that the resource is in the deleted state. This key is only used in a limited number
|
* If present and populated with a date/time (as an instance of {@link InstantDt}), this value is an indication that the resource is in the deleted state. This key is only used in a limited number
|
||||||
* of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
|
* of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
|
||||||
|
@ -71,29 +89,19 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
||||||
* Values for this key are of type <b>{@link InstantDt}</b>
|
* Values for this key are of type <b>{@link InstantDt}</b>
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public static final ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>> DELETED_AT = new ResourceMetadataKeySupportingAnyResource<InstantDt, IPrimitiveType<Date>>("DELETED_AT") {
|
public static final ResourceMetadataKeyEnum<IPrimitiveType<Date>> DELETED_AT = new ResourceMetadataKeyEnum<>("DELETED_AT") {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InstantDt get(IResource theResource) {
|
public IPrimitiveType<Date> get(IResource theResource) {
|
||||||
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
|
return (IPrimitiveType<Date>) theResource.getResourceMetadata().get(this);
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public IPrimitiveType<Date> get(IAnyResource theResource) {
|
|
||||||
return (IPrimitiveType<Date>) theResource.getUserData(DELETED_AT.name());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void put(IResource theResource, InstantDt theObject) {
|
public void put(IResource theResource, IPrimitiveType<Date> theObject) {
|
||||||
theResource.getResourceMetadata().put(DELETED_AT, theObject);
|
theResource.getResourceMetadata().put(this, theObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(IAnyResource theResource, IPrimitiveType<Date> theObject) {
|
|
||||||
theResource.setUserData(DELETED_AT.name(), theObject);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package ca.uhn.fhir.model.api;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2022 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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This enum contains the allowable codes in the HAPI FHIR defined
|
||||||
|
* codesystem: https://hapifhir.io/fhir/CodeSystem/hapi-fhir-storage-response-code
|
||||||
|
*
|
||||||
|
* This is used in CRUD response OperationOutcome resources.
|
||||||
|
*/
|
||||||
|
public enum StorageResponseCodeEnum implements ICodingEnum {
|
||||||
|
|
||||||
|
SUCCESSFUL_CREATE("Create succeeded."),
|
||||||
|
SUCCESSFUL_CREATE_NO_CONDITIONAL_MATCH("Conditional create succeeded: no existing resource matched the conditional URL."),
|
||||||
|
SUCCESSFUL_CREATE_WITH_CONDITIONAL_MATCH("Conditional create succeeded: an existing resource matched the conditional URL so no action was taken."),
|
||||||
|
SUCCESSFUL_UPDATE("Update succeeded."),
|
||||||
|
SUCCESSFUL_UPDATE_AS_CREATE("Update as create succeeded."),
|
||||||
|
SUCCESSFUL_UPDATE_NO_CHANGE("Update succeeded: No changes were detected so no action was taken."),
|
||||||
|
SUCCESSFUL_UPDATE_NO_CONDITIONAL_MATCH("Conditional update succeeded: no existing resource matched the conditional URL so a new resource was created."),
|
||||||
|
SUCCESSFUL_UPDATE_WITH_CONDITIONAL_MATCH("Conditional update succeeded: an existing resource matched the conditional URL and was updated."),
|
||||||
|
SUCCESSFUL_UPDATE_WITH_CONDITIONAL_MATCH_NO_CHANGE("Conditional update succeeded: an existing resource matched the conditional URL and was updated, but no changes were detected so no action was taken."),
|
||||||
|
SUCCESSFUL_DELETE("Delete succeeded."),
|
||||||
|
SUCCESSFUL_DELETE_ALREADY_DELETED("Delete succeeded: Resource was already deleted so no action was taken."),
|
||||||
|
SUCCESSFUL_DELETE_NOT_FOUND("Delete succeeded: No existing resource was found so no action was taken."),
|
||||||
|
|
||||||
|
SUCCESSFUL_PATCH("Patch succeeded."),
|
||||||
|
|
||||||
|
SUCCESSFUL_PATCH_NO_CHANGE("Patch succeeded: No changes were detected so no action was taken."),
|
||||||
|
SUCCESSFUL_CONDITIONAL_PATCH("Conditional patch succeeded."),
|
||||||
|
SUCCESSFUL_CONDITIONAL_PATCH_NO_CHANGE("Conditional patch succeeded: No changes were detected so no action was taken.");
|
||||||
|
|
||||||
|
public static final String SYSTEM = "https://hapifhir.io/fhir/CodeSystem/hapi-fhir-storage-response-code";
|
||||||
|
|
||||||
|
private final String myDisplay;
|
||||||
|
|
||||||
|
StorageResponseCodeEnum(String theDisplay) {
|
||||||
|
myDisplay = theDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return name();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSystem() {
|
||||||
|
return SYSTEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplay() {
|
||||||
|
return myDisplay;
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,9 +29,12 @@ import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
|
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +43,7 @@ import java.util.Objects;
|
||||||
* (method and search).
|
* (method and search).
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
*
|
* <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.
|
||||||
*
|
*
|
||||||
|
@ -101,10 +104,8 @@ public class BundleBuilder {
|
||||||
/**
|
/**
|
||||||
* Sets the specified primitive field on the bundle with the value provided.
|
* Sets the specified primitive field on the bundle with the value provided.
|
||||||
*
|
*
|
||||||
* @param theFieldName
|
* @param theFieldName Name of the primitive field.
|
||||||
* Name of the primitive field.
|
* @param theFieldValue Value of the field to be set.
|
||||||
* @param theFieldValue
|
|
||||||
* Value of the field to be set.
|
|
||||||
*/
|
*/
|
||||||
public BundleBuilder setBundleField(String theFieldName, String theFieldValue) {
|
public BundleBuilder setBundleField(String theFieldName, String theFieldValue) {
|
||||||
BaseRuntimeChildDefinition typeChild = myBundleDef.getChildByName(theFieldName);
|
BaseRuntimeChildDefinition typeChild = myBundleDef.getChildByName(theFieldName);
|
||||||
|
@ -119,12 +120,9 @@ public class BundleBuilder {
|
||||||
/**
|
/**
|
||||||
* Sets the specified primitive field on the search entry with the value provided.
|
* Sets the specified primitive field on the search entry with the value provided.
|
||||||
*
|
*
|
||||||
* @param theSearch
|
* @param theSearch Search part of the entry
|
||||||
* Search part of the entry
|
* @param theFieldName Name of the primitive field.
|
||||||
* @param theFieldName
|
* @param theFieldValue Value of the field to be set.
|
||||||
* Name of the primitive field.
|
|
||||||
* @param theFieldValue
|
|
||||||
* Value of the field to be set.
|
|
||||||
*/
|
*/
|
||||||
public BundleBuilder setSearchField(IBase theSearch, String theFieldName, String theFieldValue) {
|
public BundleBuilder setSearchField(IBase theSearch, String theFieldName, String theFieldValue) {
|
||||||
BaseRuntimeChildDefinition typeChild = mySearchDef.getChildByName(theFieldName);
|
BaseRuntimeChildDefinition typeChild = mySearchDef.getChildByName(theFieldName);
|
||||||
|
@ -144,6 +142,37 @@ public class BundleBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a FHIRPatch patch bundle to the transaction
|
||||||
|
* @param theTarget The target resource ID to patch
|
||||||
|
* @param thePatch The FHIRPath Parameters resource
|
||||||
|
* @since 6.3.0
|
||||||
|
*/
|
||||||
|
public PatchBuilder addTransactionFhirPatchEntry(IIdType theTarget, IBaseParameters thePatch) {
|
||||||
|
Validate.notNull(theTarget, "theTarget must not be null");
|
||||||
|
Validate.notBlank(theTarget.getResourceType(), "theTarget must contain a resource type");
|
||||||
|
Validate.notBlank(theTarget.getIdPart(), "theTarget must contain an ID");
|
||||||
|
|
||||||
|
IPrimitiveType<?> url = addAndPopulateTransactionBundleEntryRequest(thePatch, theTarget.getValue(), theTarget.toUnqualifiedVersionless().getValue(), "PATCH");
|
||||||
|
|
||||||
|
return new PatchBuilder(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a FHIRPatch patch bundle to the transaction. This method is intended for conditional PATCH operations. If you
|
||||||
|
* know the ID of the resource you wish to patch, use {@link #addTransactionFhirPatchEntry(IIdType, IBaseParameters)}
|
||||||
|
* instead.
|
||||||
|
*
|
||||||
|
* @param thePatch The FHIRPath Parameters resource
|
||||||
|
* @since 6.3.0
|
||||||
|
* @see #addTransactionFhirPatchEntry(IIdType, IBaseParameters)
|
||||||
|
*/
|
||||||
|
public PatchBuilder addTransactionFhirPatchEntry(IBaseParameters thePatch) {
|
||||||
|
IPrimitiveType<?> url = addAndPopulateTransactionBundleEntryRequest(thePatch, null, null, "PATCH");
|
||||||
|
|
||||||
|
return new PatchBuilder(url);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an entry containing an update (PUT) request.
|
* Adds an entry containing an update (PUT) request.
|
||||||
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
||||||
|
@ -151,22 +180,39 @@ public class BundleBuilder {
|
||||||
* @param theResource The resource to update
|
* @param theResource The resource to update
|
||||||
*/
|
*/
|
||||||
public UpdateBuilder addTransactionUpdateEntry(IBaseResource theResource) {
|
public UpdateBuilder addTransactionUpdateEntry(IBaseResource theResource) {
|
||||||
|
Validate.notNull(theResource, "theResource must not be null");
|
||||||
|
|
||||||
|
IIdType id = theResource.getIdElement();
|
||||||
|
if (id.hasIdPart() && !id.hasResourceType()) {
|
||||||
|
String resourceType = myContext.getResourceType(theResource);
|
||||||
|
id = id.withResourceType(resourceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
String requestUrl = id.toUnqualifiedVersionless().getValue();
|
||||||
|
String fullUrl = id.getValue();
|
||||||
|
String verb = "PUT";
|
||||||
|
|
||||||
|
IPrimitiveType<?> url = addAndPopulateTransactionBundleEntryRequest(theResource, fullUrl, requestUrl, verb);
|
||||||
|
|
||||||
|
return new UpdateBuilder(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private IPrimitiveType<?> addAndPopulateTransactionBundleEntryRequest(IBaseResource theResource, String theFullUrl, String theRequestUrl, String theHttpVerb) {
|
||||||
setBundleField("type", "transaction");
|
setBundleField("type", "transaction");
|
||||||
|
|
||||||
IBase request = addEntryAndReturnRequest(theResource);
|
IBase request = addEntryAndReturnRequest(theResource, theFullUrl);
|
||||||
|
|
||||||
// Bundle.entry.request.url
|
// Bundle.entry.request.url
|
||||||
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||||
String resourceType = myContext.getResourceType(theResource);
|
url.setValueAsString(theRequestUrl);
|
||||||
url.setValueAsString(theResource.getIdElement().toUnqualifiedVersionless().withResourceType(resourceType).getValue());
|
|
||||||
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
||||||
|
|
||||||
// Bundle.entry.request.url
|
// Bundle.entry.request.method
|
||||||
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
||||||
method.setValueAsString("PUT");
|
method.setValueAsString(theHttpVerb);
|
||||||
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
||||||
|
return url;
|
||||||
return new UpdateBuilder(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -178,7 +224,7 @@ public class BundleBuilder {
|
||||||
public CreateBuilder addTransactionCreateEntry(IBaseResource theResource) {
|
public CreateBuilder addTransactionCreateEntry(IBaseResource theResource) {
|
||||||
setBundleField("type", "transaction");
|
setBundleField("type", "transaction");
|
||||||
|
|
||||||
IBase request = addEntryAndReturnRequest(theResource);
|
IBase request = addEntryAndReturnRequest(theResource, theResource.getIdElement().getValue());
|
||||||
|
|
||||||
String resourceType = myContext.getResourceType(theResource);
|
String resourceType = myContext.getResourceType(theResource);
|
||||||
|
|
||||||
|
@ -198,15 +244,30 @@ public class BundleBuilder {
|
||||||
/**
|
/**
|
||||||
* Adds an entry containing a delete (DELETE) request.
|
* Adds an entry containing a delete (DELETE) request.
|
||||||
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
||||||
*
|
* <p>
|
||||||
* Note that the resource is only used to extract its ID and type, and the body of the resource is not included in the entry,
|
* Note that the resource is only used to extract its ID and type, and the body of the resource is not included in the entry,
|
||||||
*
|
*
|
||||||
* @param theResource The resource to delete.
|
* @param theResource The resource to delete.
|
||||||
*/
|
*/
|
||||||
public void addTransactionDeleteEntry(IBaseResource theResource) {
|
public DeleteBuilder addTransactionDeleteEntry(IBaseResource theResource) {
|
||||||
String resourceType = myContext.getResourceType(theResource);
|
String resourceType = myContext.getResourceType(theResource);
|
||||||
String idPart = theResource.getIdElement().toUnqualifiedVersionless().getIdPart();
|
String idPart = theResource.getIdElement().toUnqualifiedVersionless().getIdPart();
|
||||||
addTransactionDeleteEntry(resourceType, idPart);
|
return addTransactionDeleteEntry(resourceType, idPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an entry containing a delete (DELETE) request.
|
||||||
|
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
||||||
|
* <p>
|
||||||
|
* Note that the resource is only used to extract its ID and type, and the body of the resource is not included in the entry,
|
||||||
|
*
|
||||||
|
* @param theResourceId The resource ID to delete.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public DeleteBuilder addTransactionDeleteEntry(IIdType theResourceId) {
|
||||||
|
String resourceType = theResourceId.getResourceType();
|
||||||
|
String idPart = theResourceId.getIdPart();
|
||||||
|
return addTransactionDeleteEntry(resourceType, idPart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,24 +275,45 @@ public class BundleBuilder {
|
||||||
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
||||||
*
|
*
|
||||||
* @param theResourceType The type resource to delete.
|
* @param theResourceType The type resource to delete.
|
||||||
* @param theIdPart the ID of the resource to delete.
|
* @param theIdPart the ID of the resource to delete.
|
||||||
*/
|
*/
|
||||||
public void addTransactionDeleteEntry(String theResourceType, String theIdPart) {
|
public DeleteBuilder addTransactionDeleteEntry(String theResourceType, String theIdPart) {
|
||||||
setBundleField("type", "transaction");
|
setBundleField("type", "transaction");
|
||||||
IBase request = addEntryAndReturnRequest();
|
|
||||||
IdDt idDt = new IdDt(theIdPart);
|
IdDt idDt = new IdDt(theIdPart);
|
||||||
|
|
||||||
|
String deleteUrl = idDt.toUnqualifiedVersionless().withResourceType(theResourceType).getValue();
|
||||||
|
|
||||||
|
return addDeleteEntry(deleteUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an entry containing a delete (DELETE) request.
|
||||||
|
* Also sets the Bundle.type value to "transaction" if it is not already set.
|
||||||
|
*
|
||||||
|
* @param theMatchUrl The match URL, e.g. <code>Patient?identifier=http://foo|123</code>
|
||||||
|
* @since 6.3.0
|
||||||
|
*/
|
||||||
|
public BaseOperationBuilder addTransactionDeleteEntryConditional(String theMatchUrl) {
|
||||||
|
Validate.notBlank(theMatchUrl, "theMatchUrl must not be null or blank");
|
||||||
|
return addDeleteEntry(theMatchUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private DeleteBuilder addDeleteEntry(String theDeleteUrl) {
|
||||||
|
IBase request = addEntryAndReturnRequest();
|
||||||
|
|
||||||
// Bundle.entry.request.url
|
// Bundle.entry.request.url
|
||||||
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||||
url.setValueAsString(idDt.toUnqualifiedVersionless().withResourceType(theResourceType).getValue());
|
url.setValueAsString(theDeleteUrl);
|
||||||
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
||||||
|
|
||||||
// Bundle.entry.request.method
|
// Bundle.entry.request.method
|
||||||
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
||||||
method.setValueAsString("DELETE");
|
method.setValueAsString("DELETE");
|
||||||
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
||||||
}
|
|
||||||
|
|
||||||
|
return new DeleteBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -239,14 +321,13 @@ public class BundleBuilder {
|
||||||
*/
|
*/
|
||||||
public void addCollectionEntry(IBaseResource theResource) {
|
public void addCollectionEntry(IBaseResource theResource) {
|
||||||
setType("collection");
|
setType("collection");
|
||||||
addEntryAndReturnRequest(theResource);
|
addEntryAndReturnRequest(theResource, theResource.getIdElement().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new entry and adds it to the bundle
|
* Creates new entry and adds it to the bundle
|
||||||
*
|
*
|
||||||
* @return
|
* @return Returns the new entry.
|
||||||
* Returns the new entry.
|
|
||||||
*/
|
*/
|
||||||
public IBase addEntry() {
|
public IBase addEntry() {
|
||||||
IBase entry = myEntryDef.newInstance();
|
IBase entry = myEntryDef.newInstance();
|
||||||
|
@ -258,8 +339,7 @@ public class BundleBuilder {
|
||||||
* Creates new search instance for the specified entry
|
* Creates new search instance for the specified entry
|
||||||
*
|
*
|
||||||
* @param entry Entry to create search instance for
|
* @param entry Entry to create search instance for
|
||||||
* @return
|
* @return Returns the search instance
|
||||||
* Returns the search instance
|
|
||||||
*/
|
*/
|
||||||
public IBaseBackboneElement addSearch(IBase entry) {
|
public IBaseBackboneElement addSearch(IBase entry) {
|
||||||
IBase searchInstance = mySearchDef.newInstance();
|
IBase searchInstance = mySearchDef.newInstance();
|
||||||
|
@ -267,19 +347,14 @@ public class BundleBuilder {
|
||||||
return (IBaseBackboneElement) searchInstance;
|
return (IBaseBackboneElement) searchInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private IBase addEntryAndReturnRequest(IBaseResource theResource, String theFullUrl) {
|
||||||
*
|
|
||||||
* @param theResource
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public IBase addEntryAndReturnRequest(IBaseResource theResource) {
|
|
||||||
Validate.notNull(theResource, "theResource must not be null");
|
Validate.notNull(theResource, "theResource must not be null");
|
||||||
|
|
||||||
IBase entry = addEntry();
|
IBase entry = addEntry();
|
||||||
|
|
||||||
// Bundle.entry.fullUrl
|
// Bundle.entry.fullUrl
|
||||||
IPrimitiveType<?> fullUrl = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
IPrimitiveType<?> fullUrl = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||||
fullUrl.setValueAsString(theResource.getIdElement().getValue());
|
fullUrl.setValueAsString(theFullUrl);
|
||||||
myEntryFullUrlChild.getMutator().setValue(entry, fullUrl);
|
myEntryFullUrlChild.getMutator().setValue(entry, fullUrl);
|
||||||
|
|
||||||
// Bundle.entry.resource
|
// Bundle.entry.resource
|
||||||
|
@ -306,6 +381,15 @@ public class BundleBuilder {
|
||||||
return myBundle;
|
return myBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method which auto-casts the results of {@link #getBundle()}
|
||||||
|
*
|
||||||
|
* @since 6.3.0
|
||||||
|
*/
|
||||||
|
public <T extends IBaseBundle> T getBundleTyped() {
|
||||||
|
return (T) myBundle;
|
||||||
|
}
|
||||||
|
|
||||||
public BundleBuilder setMetaField(String theFieldName, IBase theFieldValue) {
|
public BundleBuilder setMetaField(String theFieldName, IBase theFieldValue) {
|
||||||
BaseRuntimeChildDefinition.IMutator mutator = myMetaDef.getChildByName(theFieldName).getMutator();
|
BaseRuntimeChildDefinition.IMutator mutator = myMetaDef.getChildByName(theFieldName).getMutator();
|
||||||
mutator.setValue(myBundle.getMeta(), theFieldValue);
|
mutator.setValue(myBundle.getMeta(), theFieldValue);
|
||||||
|
@ -315,12 +399,9 @@ public class BundleBuilder {
|
||||||
/**
|
/**
|
||||||
* Sets the specified entry field.
|
* Sets the specified entry field.
|
||||||
*
|
*
|
||||||
* @param theEntry
|
* @param theEntry The entry instance to set values on
|
||||||
* The entry instance to set values on
|
* @param theEntryChildName The child field name of the entry instance to be set
|
||||||
* @param theEntryChildName
|
* @param theValue The field value to set
|
||||||
* The child field name of the entry instance to be set
|
|
||||||
* @param theValue
|
|
||||||
* The field value to set
|
|
||||||
*/
|
*/
|
||||||
public void addToEntry(IBase theEntry, String theEntryChildName, IBase theValue) {
|
public void addToEntry(IBase theEntry, String theEntryChildName, IBase theValue) {
|
||||||
addToBase(theEntry, theEntryChildName, theValue, myEntryDef);
|
addToBase(theEntry, theEntryChildName, theValue, myEntryDef);
|
||||||
|
@ -329,12 +410,9 @@ public class BundleBuilder {
|
||||||
/**
|
/**
|
||||||
* Sets the specified search field.
|
* Sets the specified search field.
|
||||||
*
|
*
|
||||||
* @param theSearch
|
* @param theSearch The search instance to set values on
|
||||||
* The search instance to set values on
|
* @param theSearchFieldName The child field name of the search instance to be set
|
||||||
* @param theSearchFieldName
|
* @param theSearchFieldValue The field value to set
|
||||||
* The child field name of the search instance to be set
|
|
||||||
* @param theSearchFieldValue
|
|
||||||
* The field value to set
|
|
||||||
*/
|
*/
|
||||||
public void addToSearch(IBase theSearch, String theSearchFieldName, IBase theSearchFieldValue) {
|
public void addToSearch(IBase theSearch, String theSearchFieldName, IBase theSearchFieldValue) {
|
||||||
addToBase(theSearch, theSearchFieldName, theSearchFieldValue, mySearchDef);
|
addToBase(theSearch, theSearchFieldName, theSearchFieldValue, mySearchDef);
|
||||||
|
@ -349,12 +427,9 @@ public class BundleBuilder {
|
||||||
/**
|
/**
|
||||||
* Creates a new primitive.
|
* Creates a new primitive.
|
||||||
*
|
*
|
||||||
* @param theTypeName
|
* @param theTypeName The element type for the primitive
|
||||||
* The element type for the primitive
|
* @param <T> Actual type of the parameterized primitive type interface
|
||||||
* @param <T>
|
* @return Returns the new empty instance of the element definition.
|
||||||
* Actual type of the parameterized primitive type interface
|
|
||||||
* @return
|
|
||||||
* Returns the new empty instance of the element definition.
|
|
||||||
*/
|
*/
|
||||||
public <T> IPrimitiveType<T> newPrimitive(String theTypeName) {
|
public <T> IPrimitiveType<T> newPrimitive(String theTypeName) {
|
||||||
BaseRuntimeElementDefinition primitiveDefinition = myContext.getElementDefinition(theTypeName);
|
BaseRuntimeElementDefinition primitiveDefinition = myContext.getElementDefinition(theTypeName);
|
||||||
|
@ -365,14 +440,10 @@ public class BundleBuilder {
|
||||||
/**
|
/**
|
||||||
* Creates a new primitive instance of the specified element type.
|
* Creates a new primitive instance of the specified element type.
|
||||||
*
|
*
|
||||||
* @param theTypeName
|
* @param theTypeName Element type to create
|
||||||
* Element type to create
|
* @param theInitialValue Initial value to be set on the new instance
|
||||||
* @param theInitialValue
|
* @param <T> Actual type of the parameterized primitive type interface
|
||||||
* Initial value to be set on the new instance
|
* @return Returns the newly created instance
|
||||||
* @param <T>
|
|
||||||
* Actual type of the parameterized primitive type interface
|
|
||||||
* @return
|
|
||||||
* Returns the newly created instance
|
|
||||||
*/
|
*/
|
||||||
public <T> IPrimitiveType<T> newPrimitive(String theTypeName, T theInitialValue) {
|
public <T> IPrimitiveType<T> newPrimitive(String theTypeName, T theInitialValue) {
|
||||||
IPrimitiveType<T> retVal = newPrimitive(theTypeName);
|
IPrimitiveType<T> retVal = newPrimitive(theTypeName);
|
||||||
|
@ -389,38 +460,84 @@ public class BundleBuilder {
|
||||||
setBundleField("type", theType);
|
setBundleField("type", theType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpdateBuilder {
|
|
||||||
|
|
||||||
private final IPrimitiveType<?> myUrl;
|
public class DeleteBuilder extends BaseOperationBuilder {
|
||||||
|
|
||||||
public UpdateBuilder(IPrimitiveType<?> theUrl) {
|
// nothing yet
|
||||||
myUrl = theUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make this update a Conditional Update
|
|
||||||
*/
|
|
||||||
public void conditional(String theConditionalUrl) {
|
|
||||||
myUrl.setValueAsString(theConditionalUrl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CreateBuilder {
|
|
||||||
|
public class PatchBuilder extends BaseOperationBuilderWithConditionalUrl<PatchBuilder> {
|
||||||
|
|
||||||
|
PatchBuilder(IPrimitiveType<?> theUrl) {
|
||||||
|
super(theUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateBuilder extends BaseOperationBuilderWithConditionalUrl<UpdateBuilder> {
|
||||||
|
UpdateBuilder(IPrimitiveType<?> theUrl) {
|
||||||
|
super(theUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateBuilder extends BaseOperationBuilder {
|
||||||
private final IBase myRequest;
|
private final IBase myRequest;
|
||||||
|
|
||||||
public CreateBuilder(IBase theRequest) {
|
CreateBuilder(IBase theRequest) {
|
||||||
myRequest = theRequest;
|
myRequest = theRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make this create a Conditional Create
|
* Make this create a Conditional Create
|
||||||
*/
|
*/
|
||||||
public void conditional(String theConditionalUrl) {
|
public CreateBuilder conditional(String theConditionalUrl) {
|
||||||
BaseRuntimeElementDefinition<?> stringDefinition = Objects.requireNonNull(myContext.getElementDefinition("string"));
|
BaseRuntimeElementDefinition<?> stringDefinition = Objects.requireNonNull(myContext.getElementDefinition("string"));
|
||||||
IPrimitiveType<?> ifNoneExist = (IPrimitiveType<?>) stringDefinition.newInstance();
|
IPrimitiveType<?> ifNoneExist = (IPrimitiveType<?>) stringDefinition.newInstance();
|
||||||
ifNoneExist.setValueAsString(theConditionalUrl);
|
ifNoneExist.setValueAsString(theConditionalUrl);
|
||||||
|
|
||||||
myEntryRequestIfNoneExistChild.getMutator().setValue(myRequest, ifNoneExist);
|
myEntryRequestIfNoneExistChild.getMutator().setValue(myRequest, ifNoneExist);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class BaseOperationBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a reference to the BundleBuilder instance.
|
||||||
|
*
|
||||||
|
* Calling this method has no effect at all, it is only
|
||||||
|
* provided for easy method chaning if you want to build
|
||||||
|
* your bundle as a single fluent call.
|
||||||
|
*
|
||||||
|
* @since 6.3.0
|
||||||
|
*/
|
||||||
|
public BundleBuilder andThen() {
|
||||||
|
return BundleBuilder.this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class BaseOperationBuilderWithConditionalUrl<T extends BaseOperationBuilder> extends BaseOperationBuilder {
|
||||||
|
|
||||||
|
private final IPrimitiveType<?> myUrl;
|
||||||
|
|
||||||
|
BaseOperationBuilderWithConditionalUrl(IPrimitiveType<?> theUrl) {
|
||||||
|
myUrl = theUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make this update a Conditional Update
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T conditional(String theConditionalUrl) {
|
||||||
|
myUrl.setValueAsString(theConditionalUrl);
|
||||||
|
return (T) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,9 @@ import org.slf4j.LoggerFactory;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
@ -86,6 +89,10 @@ public class ClasspathUtil {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Reader loadResourceAsReader(String theClasspath) {
|
||||||
|
return new InputStreamReader(loadResourceAsStream(theClasspath), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a classpath resource, throw an {@link InternalErrorException} if not found
|
* Load a classpath resource, throw an {@link InternalErrorException} if not found
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -29,11 +29,13 @@ import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
@ -53,8 +55,12 @@ public class OperationOutcomeUtil {
|
||||||
* @return Returns the newly added issue
|
* @return Returns the newly added issue
|
||||||
*/
|
*/
|
||||||
public static IBase addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
|
public static IBase addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
|
||||||
|
return addIssue(theCtx, theOperationOutcome, theSeverity, theDetails, theLocation, theCode, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IBase addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode, @Nullable String theDetailSystem, @Nullable String theDetailCode, @Nullable String theDetailDescription) {
|
||||||
IBase issue = createIssue(theCtx, theOperationOutcome);
|
IBase issue = createIssue(theCtx, theOperationOutcome);
|
||||||
populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode);
|
populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode, theDetailSystem, theDetailCode, theDetailDescription);
|
||||||
return issue;
|
return issue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,17 +133,17 @@ public class OperationOutcomeUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void populateDetails(FhirContext theCtx, IBase theIssue, String theSeverity, String theDetails, String theLocation, String theCode) {
|
private static void populateDetails(FhirContext theCtx, IBase theIssue, String theSeverity, String theDetails, String theLocation, String theCode, String theDetailSystem, String theDetailCode, String theDetailDescription) {
|
||||||
BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theIssue.getClass());
|
BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theIssue.getClass());
|
||||||
BaseRuntimeChildDefinition detailsChild;
|
BaseRuntimeChildDefinition diagnosticsChild;
|
||||||
detailsChild = issueElement.getChildByName("diagnostics");
|
diagnosticsChild = issueElement.getChildByName("diagnostics");
|
||||||
|
|
||||||
BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code");
|
BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code");
|
||||||
IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments());
|
IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments());
|
||||||
codeElem.setValueAsString(theCode);
|
codeElem.setValueAsString(theCode);
|
||||||
codeChild.getMutator().addValue(theIssue, codeElem);
|
codeChild.getMutator().addValue(theIssue, codeElem);
|
||||||
|
|
||||||
BaseRuntimeElementDefinition<?> stringDef = detailsChild.getChildByName(detailsChild.getElementName());
|
BaseRuntimeElementDefinition<?> stringDef = diagnosticsChild.getChildByName(diagnosticsChild.getElementName());
|
||||||
BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
|
BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
|
||||||
|
|
||||||
IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments());
|
IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments());
|
||||||
|
@ -146,9 +152,27 @@ public class OperationOutcomeUtil {
|
||||||
|
|
||||||
IPrimitiveType<?> string = (IPrimitiveType<?>) stringDef.newInstance();
|
IPrimitiveType<?> string = (IPrimitiveType<?>) stringDef.newInstance();
|
||||||
string.setValueAsString(theDetails);
|
string.setValueAsString(theDetails);
|
||||||
detailsChild.getMutator().setValue(theIssue, string);
|
diagnosticsChild.getMutator().setValue(theIssue, string);
|
||||||
|
|
||||||
addLocationToIssue(theCtx, theIssue, theLocation);
|
addLocationToIssue(theCtx, theIssue, theLocation);
|
||||||
|
|
||||||
|
if (isNotBlank(theDetailSystem)) {
|
||||||
|
BaseRuntimeChildDefinition detailsChild = issueElement.getChildByName("details");
|
||||||
|
if (detailsChild != null) {
|
||||||
|
BaseRuntimeElementDefinition<?> codeableConceptDef = theCtx.getElementDefinition("CodeableConcept");
|
||||||
|
IBase codeableConcept = codeableConceptDef.newInstance();
|
||||||
|
|
||||||
|
BaseRuntimeElementDefinition<?> codingDef = theCtx.getElementDefinition("Coding");
|
||||||
|
IBaseCoding coding = (IBaseCoding) codingDef.newInstance();
|
||||||
|
coding.setSystem(theDetailSystem);
|
||||||
|
coding.setCode(theDetailCode);
|
||||||
|
coding.setDisplay(theDetailDescription);
|
||||||
|
|
||||||
|
codeableConceptDef.getChildByName("coding").getMutator().addValue(codeableConcept, coding);
|
||||||
|
|
||||||
|
detailsChild.getMutator().addValue(theIssue, codeableConcept);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addLocationToIssue(FhirContext theContext, IBase theIssue, String theLocation) {
|
public static void addLocationToIssue(FhirContext theContext, IBase theIssue, String theLocation) {
|
||||||
|
|
|
@ -107,6 +107,7 @@ public enum VersionEnum {
|
||||||
V6_1_4,
|
V6_1_4,
|
||||||
V6_2_0,
|
V6_2_0,
|
||||||
V6_2_1,
|
V6_2_1,
|
||||||
|
// Dev Build
|
||||||
V6_3_0
|
V6_3_0
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -99,10 +99,22 @@ ca.uhn.fhir.jpa.dao.BaseStorageDao.invalidParameterChain=Invalid parameter chain
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.invalidVersion=Version "{0}" is not valid for resource {1}
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.invalidVersion=Version "{0}" is not valid for resource {1}
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.multipleParamsWithSameNameOneIsMissingTrue=This server does not know how to handle multiple "{0}" parameters where one has a value of :missing=true
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.multipleParamsWithSameNameOneIsMissingTrue=This server does not know how to handle multiple "{0}" parameters where one has a value of :missing=true
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.missingBody=No body was supplied in request
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.missingBody=No body was supplied in request
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Deletion failed.
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Nothing has been deleted.
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreate=Successfully created resource "{0}" in {1}ms
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreate=Successfully created resource "{0}".
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdate=Successfully updated resource "{0}" in {1}ms
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreateConditionalNoMatch=Successfully conditionally created resource "{0}". No existing resources matched URL "{1}".
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreateConditionalWithMatch=Successfully conditionally created resource "{0}". Existing resource matched URL "{1}".
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulPatch=Successfully patched resource "{0}".
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulPatchNoChange=Successfully patched resource "{0}" with no changes detected.
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulPatchConditional=Successfully conditionally patched resource. Existing resource {0} matched URL: {1}.
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulPatchConditionalNoChange=Successfully conditionally patched resource with no changes detected. Existing resource {0} matched URL: {1}.
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdate=Successfully updated resource "{0}".
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdateNoChange=Successfully updated resource "{0}" with no changes detected.
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdateAsCreate=Successfully created resource "{0}" using update as create (ie. create with client assigned ID).
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdateConditionalWithMatch=Successfully conditionally updated resource "{0}". Existing resource matched URL "{1}".
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdateConditionalNoMatch=Successfully conditionally updated resource "{0}". Created resource because no existing resource matched URL "{1}".
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdateConditionalNoChangeWithMatch=Successfully conditionally updated resource "{0}" with no changes detected. Existing resource matched URL "{1}".
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulDeletes=Successfully deleted {0} resource(s).
|
||||||
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulTimingSuffix=Took {0}ms.
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.deleteResourceNotExisting=Not deleted, resource {0} does not exist.
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.deleteResourceNotExisting=Not deleted, resource {0} does not exist.
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.deleteResourceAlreadyDeleted=Not deleted, resource {0} was already deleted.
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.deleteResourceAlreadyDeleted=Not deleted, resource {0} was already deleted.
|
||||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.invalidSearchParameter=Unknown search parameter "{0}" for resource type "{1}". Valid search parameters for this search are: {2}
|
ca.uhn.fhir.jpa.dao.BaseStorageDao.invalidSearchParameter=Unknown search parameter "{0}" for resource type "{1}". Valid search parameters for this search are: {2}
|
||||||
|
@ -130,7 +142,7 @@ ca.uhn.fhir.jpa.search.builder.predicate.TokenPredicateBuilder.invalidCodeMissin
|
||||||
ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.matchesFound=Matches found
|
ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.matchesFound=Matches found
|
||||||
ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.noMatchesFound=No Matches found
|
ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl.noMatchesFound=No Matches found
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4.invalidSearchParamExpression=The expression "{0}" can not be evaluated and may be invalid: {1}
|
ca.uhn.fhir.jpa.dao.JpaResourceDaoSearchParameter.invalidSearchParamExpression=The expression "{0}" can not be evaluated and may be invalid: {1}
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.search.builder.QueryStack.textModifierDisabledForSearchParam=The :text modifier is disabled for this search parameter
|
ca.uhn.fhir.jpa.search.builder.QueryStack.textModifierDisabledForSearchParam=The :text modifier is disabled for this search parameter
|
||||||
ca.uhn.fhir.jpa.search.builder.QueryStack.textModifierDisabledForServer=The :text modifier is disabled on this server
|
ca.uhn.fhir.jpa.search.builder.QueryStack.textModifierDisabledForServer=The :text modifier is disabled on this server
|
||||||
|
|
|
@ -32,6 +32,15 @@ public class ClasspathUtilTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoadResourceAsReaderNotFound() {
|
||||||
|
try {
|
||||||
|
ClasspathUtil.loadResourceAsReader("/FOOOOOO");
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
assertEquals(Msg.code(1758) + "Unable to find classpath resource: /FOOOOOO", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should not throw any exception
|
* Should not throw any exception
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,6 +8,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.hasItem;
|
import static org.hamcrest.CoreMatchers.hasItem;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class VersionEnumTest {
|
public class VersionEnumTest {
|
||||||
|
|
||||||
|
@ -18,10 +19,25 @@ public class VersionEnumTest {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
String version = VersionUtil.getVersion();
|
String version = VersionUtil.getVersion();
|
||||||
version = "V" + version.replace(".", "_");
|
|
||||||
version = version.replaceAll("-PRE[0-9]+", "");
|
version = version.replaceAll("-PRE[0-9]+", "");
|
||||||
version = version.replace("-SNAPSHOT", "");
|
version = version.replace("-SNAPSHOT", "");
|
||||||
|
|
||||||
|
String[] parts = version.split("\\.");
|
||||||
|
assertEquals(3, parts.length);
|
||||||
|
int major = Integer.parseInt(parts[0]);
|
||||||
|
int minor = Integer.parseInt(parts[1]);
|
||||||
|
int patch = Integer.parseInt(parts[2]);
|
||||||
|
|
||||||
|
if (major >= 6 && minor >= 3) {
|
||||||
|
if (minor % 2 == 1) {
|
||||||
|
patch = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
version = major + "." + minor + "." + patch;
|
||||||
|
|
||||||
|
version = "V" + version.replace(".", "_");
|
||||||
|
|
||||||
assertThat(versions, hasItem(version));
|
assertThat(versions, hasItem(version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-bom</artifactId>
|
<artifactId>hapi-fhir-bom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>HAPI FHIR BOM</name>
|
<name>HAPI FHIR BOM</name>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-cli</artifactId>
|
<artifactId>hapi-fhir-cli</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../../hapi-deployable-pom</relativePath>
|
<relativePath>../../hapi-deployable-pom</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -47,9 +47,12 @@ import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
|
import org.hl7.fhir.r5.model.SearchParameter;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class converts versions of various resources to/from a canonical version
|
* This class converts versions of various resources to/from a canonical version
|
||||||
* of the resource. The specific version that is considered canonical is arbitrary
|
* of the resource. The specific version that is considered canonical is arbitrary
|
||||||
|
@ -68,14 +71,13 @@ public class VersionCanonicalizer {
|
||||||
private static final BaseAdvisor_10_50 ADVISOR_10_50 = new BaseAdvisor_10_50(false);
|
private static final BaseAdvisor_10_50 ADVISOR_10_50 = new BaseAdvisor_10_50(false);
|
||||||
private static final BaseAdvisor_40_50 ADVISOR_40_50 = new BaseAdvisor_40_50(false);
|
private static final BaseAdvisor_40_50 ADVISOR_40_50 = new BaseAdvisor_40_50(false);
|
||||||
private static final BaseAdvisor_43_50 ADVISOR_43_50 = new BaseAdvisor_43_50(false);
|
private static final BaseAdvisor_43_50 ADVISOR_43_50 = new BaseAdvisor_43_50(false);
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private final IStrategy myStrategy;
|
private final IStrategy myStrategy;
|
||||||
|
|
||||||
public VersionCanonicalizer(FhirContext theTargetContext) {
|
public VersionCanonicalizer(FhirContext theTargetContext) {
|
||||||
this(theTargetContext.getVersion().getVersion());
|
this(theTargetContext.getVersion().getVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
@SuppressWarnings({"EnumSwitchStatementWhichMissesCases", "EnhancedSwitchMigration"})
|
||||||
public VersionCanonicalizer(FhirVersionEnum theTargetVersion) {
|
public VersionCanonicalizer(FhirVersionEnum theTargetVersion) {
|
||||||
switch (theTargetVersion) {
|
switch (theTargetVersion) {
|
||||||
case DSTU2:
|
case DSTU2:
|
||||||
|
@ -160,9 +162,13 @@ public class VersionCanonicalizer {
|
||||||
return myStrategy.conceptMapToCanonical(theConceptMap);
|
return myStrategy.conceptMapToCanonical(theConceptMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface IStrategy<T extends IBaseResource> {
|
public <T extends IBaseResource> SearchParameter searchParameterToCanonical(T theSearchParameter) {
|
||||||
|
return myStrategy.searchParameterToCanonical(theSearchParameter);
|
||||||
|
}
|
||||||
|
|
||||||
CapabilityStatement capabilityStatementToCanonical(T theCapabilityStatement);
|
private interface IStrategy {
|
||||||
|
|
||||||
|
CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement);
|
||||||
|
|
||||||
Coding codingToCanonical(IBaseCoding theCoding);
|
Coding codingToCanonical(IBaseCoding theCoding);
|
||||||
|
|
||||||
|
@ -175,9 +181,11 @@ public class VersionCanonicalizer {
|
||||||
IBaseResource valueSetFromCanonical(ValueSet theValueSet);
|
IBaseResource valueSetFromCanonical(ValueSet theValueSet);
|
||||||
|
|
||||||
ConceptMap conceptMapToCanonical(IBaseResource theConceptMap);
|
ConceptMap conceptMapToCanonical(IBaseResource theConceptMap);
|
||||||
|
|
||||||
|
SearchParameter searchParameterToCanonical(IBaseResource theSearchParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Dstu2Strategy implements IStrategy<ca.uhn.fhir.model.dstu2.resource.BaseResource> {
|
private static class Dstu2Strategy implements IStrategy {
|
||||||
|
|
||||||
private final FhirContext myDstu2Hl7OrgContext = FhirContext.forDstu2Hl7OrgCached();
|
private final FhirContext myDstu2Hl7OrgContext = FhirContext.forDstu2Hl7OrgCached();
|
||||||
|
|
||||||
|
@ -189,7 +197,7 @@ public class VersionCanonicalizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CapabilityStatement capabilityStatementToCanonical(ca.uhn.fhir.model.dstu2.resource.BaseResource theCapabilityStatement) {
|
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||||
org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theCapabilityStatement);
|
org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theCapabilityStatement);
|
||||||
return (CapabilityStatement) VersionConvertorFactory_10_50.convertResource(reencoded, ADVISOR_10_50);
|
return (CapabilityStatement) VersionConvertorFactory_10_50.convertResource(reencoded, ADVISOR_10_50);
|
||||||
}
|
}
|
||||||
|
@ -224,8 +232,7 @@ public class VersionCanonicalizer {
|
||||||
@Override
|
@Override
|
||||||
public ValueSet valueSetToCanonical(IBaseResource theValueSet) {
|
public ValueSet valueSetToCanonical(IBaseResource theValueSet) {
|
||||||
org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theValueSet);
|
org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theValueSet);
|
||||||
ValueSet valueSet = (ValueSet) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
|
return (ValueSet) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
|
||||||
return valueSet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -275,6 +282,16 @@ public class VersionCanonicalizer {
|
||||||
return (ConceptMap) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
|
return (ConceptMap) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchParameter searchParameterToCanonical(IBaseResource theSearchParameter) {
|
||||||
|
org.hl7.fhir.dstu2.model.SearchParameter reencoded = (org.hl7.fhir.dstu2.model.SearchParameter) reencodeToHl7Org(theSearchParameter);
|
||||||
|
SearchParameter retVal = (SearchParameter) VersionConvertorFactory_10_50.convertResource(reencoded, ADVISOR_10_50);
|
||||||
|
if (isBlank(retVal.getExpression())) {
|
||||||
|
retVal.setExpression(reencoded.getXpath());
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
private Resource reencodeToHl7Org(IBaseResource theInput) {
|
private Resource reencodeToHl7Org(IBaseResource theInput) {
|
||||||
if (myHl7OrgStructures) {
|
if (myHl7OrgStructures) {
|
||||||
return (Resource) theInput;
|
return (Resource) theInput;
|
||||||
|
@ -291,11 +308,11 @@ public class VersionCanonicalizer {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Dstu3Strategy implements IStrategy<org.hl7.fhir.dstu3.model.Resource> {
|
private static class Dstu3Strategy implements IStrategy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.dstu3.model.Resource theCapabilityStatement) {
|
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||||
return (CapabilityStatement) VersionConvertorFactory_30_50.convertResource(theCapabilityStatement, ADVISOR_30_50);
|
return (CapabilityStatement) VersionConvertorFactory_30_50.convertResource((org.hl7.fhir.dstu3.model.Resource) theCapabilityStatement, ADVISOR_30_50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -327,12 +344,17 @@ public class VersionCanonicalizer {
|
||||||
public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) {
|
public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) {
|
||||||
return (ConceptMap) VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theConceptMap, ADVISOR_30_40);
|
return (ConceptMap) VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theConceptMap, ADVISOR_30_40);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchParameter searchParameterToCanonical(IBaseResource theSearchParameter) {
|
||||||
|
return (SearchParameter) VersionConvertorFactory_30_50.convertResource((org.hl7.fhir.dstu3.model.Resource) theSearchParameter, ADVISOR_30_50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class R4Strategy implements IStrategy<org.hl7.fhir.r4.model.Resource> {
|
private static class R4Strategy implements IStrategy {
|
||||||
@Override
|
@Override
|
||||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r4.model.Resource theCapabilityStatement) {
|
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||||
return (CapabilityStatement) VersionConvertorFactory_40_50.convertResource(theCapabilityStatement, ADVISOR_40_50);
|
return (CapabilityStatement) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r4.model.Resource) theCapabilityStatement, ADVISOR_40_50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -365,13 +387,18 @@ public class VersionCanonicalizer {
|
||||||
return (ConceptMap) theConceptMap;
|
return (ConceptMap) theConceptMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchParameter searchParameterToCanonical(IBaseResource theSearchParameter) {
|
||||||
|
return (SearchParameter) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r4.model.Resource) theSearchParameter, ADVISOR_40_50);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class R4BStrategy implements IStrategy<org.hl7.fhir.r4b.model.Resource> {
|
private static class R4BStrategy implements IStrategy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r4b.model.Resource theCapabilityStatement) {
|
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||||
return (CapabilityStatement) VersionConvertorFactory_43_50.convertResource(theCapabilityStatement, ADVISOR_43_50);
|
return (CapabilityStatement) VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theCapabilityStatement, ADVISOR_43_50);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -410,13 +437,18 @@ public class VersionCanonicalizer {
|
||||||
return (ConceptMap) VersionConvertorFactory_40_50.convertResource(conceptMapR5, ADVISOR_40_50);
|
return (ConceptMap) VersionConvertorFactory_40_50.convertResource(conceptMapR5, ADVISOR_40_50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchParameter searchParameterToCanonical(IBaseResource theSearchParameter) {
|
||||||
|
return (SearchParameter) VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theSearchParameter, ADVISOR_43_50);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class R5Strategy implements IStrategy<org.hl7.fhir.r5.model.Resource> {
|
private static class R5Strategy implements IStrategy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r5.model.Resource theCapabilityStatement) {
|
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||||
return (CapabilityStatement) theCapabilityStatement;
|
return (CapabilityStatement) theCapabilityStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,6 +482,11 @@ public class VersionCanonicalizer {
|
||||||
return (ConceptMap) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.ConceptMap) theConceptMap, ADVISOR_40_50);
|
return (ConceptMap) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.ConceptMap) theConceptMap, ADVISOR_40_50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SearchParameter searchParameterToCanonical(IBaseResource theSearchParameter) {
|
||||||
|
return (SearchParameter) theSearchParameter;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,11 @@ import ca.uhn.fhir.util.BundleBuilder;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
@ -109,6 +114,53 @@ public class BundleBuilderExamples {
|
||||||
//END SNIPPET: createConditional
|
//END SNIPPET: createConditional
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void patch() throws FHIRException {
|
||||||
|
//START SNIPPET: patch
|
||||||
|
|
||||||
|
// Create a FHIR Patch object
|
||||||
|
Parameters patch = new Parameters();
|
||||||
|
Parameters.ParametersParameterComponent op = patch.addParameter().setName("operation");
|
||||||
|
op.addPart().setName("type").setValue(new CodeType("replace"));
|
||||||
|
op.addPart().setName("path").setValue(new CodeType("Patient.active"));
|
||||||
|
op.addPart().setName("value").setValue(new BooleanType(false));
|
||||||
|
|
||||||
|
// Create a TransactionBuilder
|
||||||
|
BundleBuilder builder = new BundleBuilder(myFhirContext);
|
||||||
|
|
||||||
|
// Create a target object (this is the ID of the resource that will be patched)
|
||||||
|
IIdType targetId = new IdType("Patient/123");
|
||||||
|
|
||||||
|
// Add the patch to the bundle
|
||||||
|
builder.addTransactionFhirPatchEntry(targetId, patch);
|
||||||
|
|
||||||
|
// Execute the transaction
|
||||||
|
IBaseBundle outcome = myFhirClient.transaction().withBundle(builder.getBundle()).execute();
|
||||||
|
//END SNIPPET: patch
|
||||||
|
}
|
||||||
|
|
||||||
|
public void patchConditional() throws FHIRException {
|
||||||
|
//START SNIPPET: patchConditional
|
||||||
|
|
||||||
|
// Create a FHIR Patch object
|
||||||
|
Parameters patch = new Parameters();
|
||||||
|
Parameters.ParametersParameterComponent op = patch.addParameter().setName("operation");
|
||||||
|
op.addPart().setName("type").setValue(new CodeType("replace"));
|
||||||
|
op.addPart().setName("path").setValue(new CodeType("Patient.active"));
|
||||||
|
op.addPart().setName("value").setValue(new BooleanType(false));
|
||||||
|
|
||||||
|
// Create a TransactionBuilder
|
||||||
|
BundleBuilder builder = new BundleBuilder(myFhirContext);
|
||||||
|
|
||||||
|
// Add the patch to the bundle with a conditional URL
|
||||||
|
String conditionalUrl = "Patient?identifier=http://foo|123";
|
||||||
|
builder.addTransactionFhirPatchEntry(patch).conditional(conditionalUrl);
|
||||||
|
|
||||||
|
|
||||||
|
// Execute the transaction
|
||||||
|
IBaseBundle outcome = myFhirClient.transaction().withBundle(builder.getBundle()).execute();
|
||||||
|
//END SNIPPET: patchConditional
|
||||||
|
}
|
||||||
|
|
||||||
public void customizeBundle() throws FHIRException {
|
public void customizeBundle() throws FHIRException {
|
||||||
//START SNIPPET: customizeBundle
|
//START SNIPPET: customizeBundle
|
||||||
// Create a TransactionBuilder
|
// Create a TransactionBuilder
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
---
|
---
|
||||||
release-date: "FILL ME"
|
release-date: "2022-07-08"
|
||||||
codename: "Sojourner"
|
codename: "Sojourner"
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 4293
|
||||||
|
title: "The BundleBuilder now supports adding conditional
|
||||||
|
DELETE operations, PATCH operations, and conditional PATCH
|
||||||
|
operations to a transaction bundle."
|
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 4293
|
||||||
|
title: "When performing create/update/patch/delete operations against the JPA server, the response
|
||||||
|
OperationOutcome will now include additional details about the outcome of the operation. This
|
||||||
|
includes:
|
||||||
|
<ul>
|
||||||
|
<li>For updates, the message will indicate the the update did not contain any changes (i.e. a No-op)</li>
|
||||||
|
<li>For conditional creates/updates/deletes, the message will indicate whether the conditional URL matched any existing resources and the outcome of the operation.</li>
|
||||||
|
<li>A new coding has been added to the <code>OperationOutcome.issue.details.coding</code> containing a machine processable equivalent to the outcome.</li>
|
||||||
|
</ul>"
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 4293
|
||||||
|
title: "When updating resources using a FHIR transaction in the JPA server, if the
|
||||||
|
client instructs the server to include the resource body in the response, any
|
||||||
|
tags that have been carried forward from previous versions of the resource are
|
||||||
|
now included in the response."
|
|
@ -6,6 +6,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li>SLF4j (All): 1.7.33 -> 2.0.3</li>
|
<li>SLF4j (All): 1.7.33 -> 2.0.3</li>
|
||||||
<li>Logback (All): 1.2.10 -> 1.4.4</li>
|
<li>Logback (All): 1.2.10 -> 1.4.4</li>
|
||||||
|
<li>Woodstox-Core (XML Parser): 6.3.1 -> 6.4.0</li>
|
||||||
<li>log4j-to-slf4j (JPA): 2.17.1 -> 2.19.0</li>
|
<li>log4j-to-slf4j (JPA): 2.17.1 -> 2.19.0</li>
|
||||||
<li>Jetty (CLI): 9.4.48.v20220622 -> 10.0.12</li>
|
<li>Jetty (CLI): 9.4.48.v20220622 -> 10.0.12</li>
|
||||||
<li>Spring Boot: 2.7.4 -> 2.7.5</li>
|
<li>Spring Boot: 2.7.4 -> 2.7.5</li>
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
The following diagram shows the request processing pipeline for processing a server request. You may click on the diagram to enlarge it.
|
The following diagram shows the request processing pipeline for processing a server request. You may click on the diagram to enlarge it.
|
||||||
|
|
||||||
<a href="/hapi-fhir/docs/images/interceptors-server-pipeline.svg" target="_blank">Expand</a>
|
<a href="server_pointcuts/interceptors-server-pipeline.svg" target="_blank">Expand</a>
|
||||||
<img src="/hapi-fhir/docs/images/interceptors-server-pipeline.svg" alt="Server Pipeline"/>
|
<img src="server_pointcuts/interceptors-server-pipeline.svg" alt="Server Pipeline"/>
|
||||||
|
|
||||||
# Storage / JPA Server Pointcuts
|
# Storage / JPA Server Pointcuts
|
||||||
|
|
||||||
The following diagram shows the request processing pipeline for processing a server request. You may click on the diagram to enlarge it.
|
The following diagram shows the request processing pipeline for processing a server request. You may click on the diagram to enlarge it.
|
||||||
|
|
||||||
<a href="/hapi-fhir/docs/images/interceptors-server-jpa-pipeline.svg" target="_blank">Expand</a>
|
<a href="server_pointcuts/interceptors-server-jpa-pipeline.svg" target="_blank">Expand</a>
|
||||||
<img src="/hapi-fhir/docs/images/interceptors-server-jpa-pipeline.svg" alt="Server Pipeline"/>
|
<img src="server_pointcuts/interceptors-server-jpa-pipeline.svg" alt="Server Pipeline"/>
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 209 KiB After Width: | Height: | Size: 209 KiB |
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 254 KiB |
|
@ -36,7 +36,23 @@ If you want to perform a conditional update:
|
||||||
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/BundleBuilderExamples.java|updateConditional}}
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/BundleBuilderExamples.java|updateConditional}}
|
||||||
```
|
```
|
||||||
|
|
||||||
# Customizing bundle
|
# Transaction Patch
|
||||||
|
|
||||||
|
To add a PATCH operation to a transaction bundle:
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/BundleBuilderExamples.java|patch}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conditional Patch
|
||||||
|
|
||||||
|
If you want to perform a conditional patch:
|
||||||
|
|
||||||
|
```java
|
||||||
|
{{snippet:classpath:/ca/uhn/hapi/fhir/docs/BundleBuilderExamples.java|patchConditional}}
|
||||||
|
```
|
||||||
|
|
||||||
|
# Customizing the Bundle
|
||||||
|
|
||||||
If you want to manipulate a bundle:
|
If you want to manipulate a bundle:
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -39,14 +39,16 @@ public class SqlQuery {
|
||||||
private final StackTraceElement[] myStackTrace;
|
private final StackTraceElement[] myStackTrace;
|
||||||
private final int mySize;
|
private final int mySize;
|
||||||
private final LanguageEnum myLanguage;
|
private final LanguageEnum myLanguage;
|
||||||
|
private final String myNamespace;
|
||||||
|
|
||||||
public SqlQuery(String theSql, List<String> theParams, long theQueryTimestamp, long theElapsedTime, StackTraceElement[] theStackTraceElements, int theSize) {
|
public SqlQuery(String theSql, List<String> theParams, long theQueryTimestamp, long theElapsedTime, StackTraceElement[] theStackTraceElements, int theSize) {
|
||||||
this(theSql, theParams, theQueryTimestamp, theElapsedTime, theStackTraceElements, theSize, LanguageEnum.SQL);
|
this(null, theSql, theParams, theQueryTimestamp, theElapsedTime, theStackTraceElements, theSize, LanguageEnum.SQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SqlQuery(String theSql, List<String> theParams, long theQueryTimestamp, long theElapsedTime, StackTraceElement[] theStackTraceElements, int theSize, LanguageEnum theLanguage) {
|
public SqlQuery(String theNamespace, String theSql, List<String> theParams, long theQueryTimestamp, long theElapsedTime, StackTraceElement[] theStackTraceElements, int theSize, LanguageEnum theLanguage) {
|
||||||
Validate.notNull(theLanguage, "theLanguage must not be null");
|
Validate.notNull(theLanguage, "theLanguage must not be null");
|
||||||
|
|
||||||
|
myNamespace = theNamespace;
|
||||||
mySql = theSql;
|
mySql = theSql;
|
||||||
myParams = Collections.unmodifiableList(theParams);
|
myParams = Collections.unmodifiableList(theParams);
|
||||||
myQueryTimestamp = theQueryTimestamp;
|
myQueryTimestamp = theQueryTimestamp;
|
||||||
|
@ -56,6 +58,10 @@ public class SqlQuery {
|
||||||
myLanguage = theLanguage;
|
myLanguage = theLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getNamespace() {
|
||||||
|
return myNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
public long getQueryTimestamp() {
|
public long getQueryTimestamp() {
|
||||||
return myQueryTimestamp;
|
return myQueryTimestamp;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +124,10 @@ public class SqlQuery {
|
||||||
return mySize;
|
return mySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getSql(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
public enum LanguageEnum {
|
public enum LanguageEnum {
|
||||||
|
|
||||||
|
@ -125,9 +135,4 @@ public class SqlQuery {
|
||||||
JSON
|
JSON
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getSql(true, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,7 +384,7 @@ public class TestUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sleepOneClick() {
|
public static void sleepOneClick() {
|
||||||
ca.uhn.fhir.util.TestUtil.sleepAtLeast(1);
|
ca.uhn.fhir.util.TestUtil.sleepAtLeast(1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
public String storeWorkChunk(BatchWorkChunk theBatchWorkChunk) {
|
public String storeWorkChunk(BatchWorkChunk theBatchWorkChunk) {
|
||||||
Batch2WorkChunkEntity entity = new Batch2WorkChunkEntity();
|
Batch2WorkChunkEntity entity = new Batch2WorkChunkEntity();
|
||||||
entity.setId(UUID.randomUUID().toString());
|
entity.setId(UUID.randomUUID().toString());
|
||||||
|
@ -102,7 +102,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
public Optional<WorkChunk> fetchWorkChunkSetStartTimeAndMarkInProgress(String theChunkId) {
|
public Optional<WorkChunk> fetchWorkChunkSetStartTimeAndMarkInProgress(String theChunkId) {
|
||||||
myWorkChunkRepository.updateChunkStatusForStart(theChunkId, new Date(), StatusEnum.IN_PROGRESS);
|
myWorkChunkRepository.updateChunkStatusForStart(theChunkId, new Date(), StatusEnum.IN_PROGRESS);
|
||||||
Optional<Batch2WorkChunkEntity> chunk = myWorkChunkRepository.findById(theChunkId);
|
Optional<Batch2WorkChunkEntity> chunk = myWorkChunkRepository.findById(theChunkId);
|
||||||
|
@ -110,7 +110,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
public String storeNewInstance(JobInstance theInstance) {
|
public String storeNewInstance(JobInstance theInstance) {
|
||||||
Validate.isTrue(isBlank(theInstance.getInstanceId()));
|
Validate.isTrue(isBlank(theInstance.getInstanceId()));
|
||||||
|
|
||||||
|
@ -271,7 +271,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRED)
|
||||||
public boolean canAdvanceInstanceToNextStep(String theInstanceId, String theCurrentStepId) {
|
public boolean canAdvanceInstanceToNextStep(String theInstanceId, String theCurrentStepId) {
|
||||||
List<StatusEnum> statusesForStep = myWorkChunkRepository.getDistinctStatusesForStep(theInstanceId, theCurrentStepId);
|
List<StatusEnum> statusesForStep = myWorkChunkRepository.getDistinctStatusesForStep(theInstanceId, theCurrentStepId);
|
||||||
ourLog.debug("Checking whether gated job can advanced to next step. [instanceId={}, currentStepId={}, statusesForStep={}]", theInstanceId, theCurrentStepId, statusesForStep);
|
ourLog.debug("Checking whether gated job can advanced to next step. [instanceId={}, currentStepId={}, statusesForStep={}]", theInstanceId, theCurrentStepId, statusesForStep);
|
||||||
|
|
|
@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSqlBuilder;
|
import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSqlBuilder;
|
||||||
import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSvcImpl;
|
import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.reindex.Batch2DaoSvcImpl;
|
import ca.uhn.fhir.jpa.reindex.Batch2DaoSvcImpl;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
@ -43,7 +44,7 @@ public class Batch2SupportConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public IDeleteExpungeSvc deleteExpungeSvc(EntityManager theEntityManager, DeleteExpungeSqlBuilder theDeleteExpungeSqlBuilder, IFulltextSearchSvc theFullTextSearchSvc) {
|
public IDeleteExpungeSvc deleteExpungeSvc(EntityManager theEntityManager, DeleteExpungeSqlBuilder theDeleteExpungeSqlBuilder, @Autowired(required = false) IFulltextSearchSvc theFullTextSearchSvc) {
|
||||||
return new DeleteExpungeSvcImpl(theEntityManager, theDeleteExpungeSqlBuilder, theFullTextSearchSvc);
|
return new DeleteExpungeSvcImpl(theEntityManager, theDeleteExpungeSqlBuilder, theFullTextSearchSvc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,11 @@ import ca.uhn.fhir.jpa.dao.DaoSearchParamProvider;
|
||||||
import ca.uhn.fhir.jpa.dao.HistoryBuilder;
|
import ca.uhn.fhir.jpa.dao.HistoryBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
|
import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
|
||||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.dao.JpaStorageResourceParser;
|
||||||
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
|
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
|
||||||
|
import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||||
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
import ca.uhn.fhir.jpa.dao.TransactionProcessor;
|
||||||
import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService;
|
import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService;
|
||||||
|
@ -116,10 +119,14 @@ import ca.uhn.fhir.jpa.searchparam.nickname.NicknameInterceptor;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
import ca.uhn.fhir.jpa.sp.SearchParamPresenceSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.TermCodeSystemStorageSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.term.TermReadSvcImpl;
|
import ca.uhn.fhir.jpa.term.TermReadSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermReindexingSvc;
|
||||||
import ca.uhn.fhir.jpa.term.config.TermCodeSystemConfig;
|
import ca.uhn.fhir.jpa.term.config.TermCodeSystemConfig;
|
||||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import ca.uhn.fhir.jpa.validation.ResourceLoaderImpl;
|
import ca.uhn.fhir.jpa.validation.ResourceLoaderImpl;
|
||||||
|
@ -225,7 +232,6 @@ public class JpaConfig {
|
||||||
return new ResponseTerminologyTranslationInterceptor(theValidationSupport, theResponseTerminologyTranslationSvc);
|
return new ResponseTerminologyTranslationInterceptor(theValidationSupport, theResponseTerminologyTranslationSvc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Lazy
|
|
||||||
@Bean
|
@Bean
|
||||||
public ResponseTerminologyTranslationSvc responseTerminologyTranslationSvc(IValidationSupport theValidationSupport) {
|
public ResponseTerminologyTranslationSvc responseTerminologyTranslationSvc(IValidationSupport theValidationSupport) {
|
||||||
return new ResponseTerminologyTranslationSvc(theValidationSupport);
|
return new ResponseTerminologyTranslationSvc(theValidationSupport);
|
||||||
|
@ -260,6 +266,11 @@ public class JpaConfig {
|
||||||
return new ValueSetOperationProvider();
|
return new ValueSetOperationProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public IJpaStorageResourceParser jpaStorageResourceParser() {
|
||||||
|
return new JpaStorageResourceParser();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public TransactionProcessor transactionProcessor() {
|
public TransactionProcessor transactionProcessor() {
|
||||||
return new TransactionProcessor();
|
return new TransactionProcessor();
|
||||||
|
@ -763,4 +774,22 @@ public class JpaConfig {
|
||||||
return new TermReadSvcImpl();
|
return new TermReadSvcImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ITermCodeSystemStorageSvc termCodeSystemStorageSvc() {
|
||||||
|
return new TermCodeSystemStorageSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ITermReindexingSvc termReindexingSvc() {
|
||||||
|
return new TermReindexingSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ObservationLastNIndexPersistSvc baseObservationLastNIndexpersistSvc() {
|
||||||
|
return new ObservationLastNIndexPersistSvc();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.config;
|
|
||||||
|
|
||||||
/*-
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc;
|
|
||||||
import ca.uhn.fhir.jpa.term.TermCodeSystemStorageSvcImpl;
|
|
||||||
import ca.uhn.fhir.jpa.term.TermConceptDaoSvc;
|
|
||||||
import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl;
|
|
||||||
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermReindexingSvc;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
public class SharedConfigDstu3Plus {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ITermCodeSystemStorageSvc termCodeSystemStorageSvc() {
|
|
||||||
return new TermCodeSystemStorageSvcImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ITermReindexingSvc termReindexingSvc() {
|
|
||||||
return new TermReindexingSvcImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ObservationLastNIndexPersistSvc baseObservationLastNIndexpersistSvc() {
|
|
||||||
return new ObservationLastNIndexPersistSvc();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.api.IDaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigDstu3;
|
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigDstu3;
|
||||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||||
import ca.uhn.fhir.jpa.config.SharedConfigDstu3Plus;
|
|
||||||
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
||||||
import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3;
|
import ca.uhn.fhir.jpa.dao.dstu3.TransactionProcessorVersionAdapterDstu3;
|
||||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||||
|
@ -53,7 +52,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
@Import({
|
@Import({
|
||||||
FhirContextDstu3Config.class,
|
FhirContextDstu3Config.class,
|
||||||
GeneratedDaoAndResourceProviderConfigDstu3.class,
|
GeneratedDaoAndResourceProviderConfigDstu3.class,
|
||||||
SharedConfigDstu3Plus.class,
|
|
||||||
JpaConfig.class
|
JpaConfig.class
|
||||||
})
|
})
|
||||||
public class JpaDstu3Config {
|
public class JpaDstu3Config {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR4;
|
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR4;
|
||||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||||
import ca.uhn.fhir.jpa.config.SharedConfigDstu3Plus;
|
|
||||||
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
||||||
import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4;
|
import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4;
|
||||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||||
|
@ -22,7 +21,6 @@ import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc;
|
||||||
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
|
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.Consent;
|
import org.hl7.fhir.r4.model.Consent;
|
||||||
|
@ -62,7 +60,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
@Import({
|
@Import({
|
||||||
FhirContextR4Config.class,
|
FhirContextR4Config.class,
|
||||||
GeneratedDaoAndResourceProviderConfigR4.class,
|
GeneratedDaoAndResourceProviderConfigR4.class,
|
||||||
SharedConfigDstu3Plus.class,
|
|
||||||
JpaConfig.class
|
JpaConfig.class
|
||||||
})
|
})
|
||||||
public class JpaR4Config {
|
public class JpaR4Config {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.api.IDaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR4B;
|
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR4B;
|
||||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||||
import ca.uhn.fhir.jpa.config.SharedConfigDstu3Plus;
|
|
||||||
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
||||||
import ca.uhn.fhir.jpa.dao.r4b.TransactionProcessorVersionAdapterR4B;
|
import ca.uhn.fhir.jpa.dao.r4b.TransactionProcessorVersionAdapterR4B;
|
||||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||||
|
@ -53,7 +52,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
@Import({
|
@Import({
|
||||||
FhirContextR4BConfig.class,
|
FhirContextR4BConfig.class,
|
||||||
GeneratedDaoAndResourceProviderConfigR4B.class,
|
GeneratedDaoAndResourceProviderConfigR4B.class,
|
||||||
SharedConfigDstu3Plus.class,
|
|
||||||
JpaConfig.class
|
JpaConfig.class
|
||||||
})
|
})
|
||||||
public class JpaR4BConfig {
|
public class JpaR4BConfig {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.api.IDaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR5;
|
import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR5;
|
||||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||||
import ca.uhn.fhir.jpa.config.SharedConfigDstu3Plus;
|
|
||||||
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
import ca.uhn.fhir.jpa.dao.ITransactionProcessorVersionAdapter;
|
||||||
import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5;
|
import ca.uhn.fhir.jpa.dao.r5.TransactionProcessorVersionAdapterR5;
|
||||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||||
|
@ -53,7 +52,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
@Import({
|
@Import({
|
||||||
FhirContextR5Config.class,
|
FhirContextR5Config.class,
|
||||||
GeneratedDaoAndResourceProviderConfigR5.class,
|
GeneratedDaoAndResourceProviderConfigR5.class,
|
||||||
SharedConfigDstu3Plus.class,
|
|
||||||
JpaConfig.class
|
JpaConfig.class
|
||||||
})
|
})
|
||||||
public class JpaR5Config {
|
public class JpaR5Config {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IJpaDao;
|
import ca.uhn.fhir.jpa.api.dao.IJpaDao;
|
||||||
|
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||||
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
|
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||||
|
@ -29,8 +30,6 @@ import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
||||||
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
|
import ca.uhn.fhir.jpa.delete.DeleteConflictService;
|
||||||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
|
||||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||||
|
@ -46,11 +45,9 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||||
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
||||||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||||
import ca.uhn.fhir.jpa.model.search.ExtendedHSearchIndexData;
|
import ca.uhn.fhir.jpa.model.search.ExtendedHSearchIndexData;
|
||||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
|
||||||
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
||||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.LogicalReferenceHelper;
|
import ca.uhn.fhir.jpa.searchparam.extractor.LogicalReferenceHelper;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
|
@ -61,6 +58,7 @@ import ca.uhn.fhir.jpa.util.AddRemoveCount;
|
||||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.StorageResponseCodeEnum;
|
||||||
import ca.uhn.fhir.model.api.Tag;
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||||
|
@ -72,13 +70,11 @@ import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.parser.LenientErrorHandler;
|
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.InterceptorInvocationTimingEnum;
|
import ca.uhn.fhir.rest.api.InterceptorInvocationTimingEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||||
import ca.uhn.fhir.rest.param.HistorySearchStyleEnum;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
||||||
|
@ -86,6 +82,8 @@ import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import ca.uhn.fhir.util.MetaUtil;
|
import ca.uhn.fhir.util.MetaUtil;
|
||||||
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import ca.uhn.fhir.util.XmlUtil;
|
import ca.uhn.fhir.util.XmlUtil;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
@ -148,11 +146,9 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.ALL_PARTITIONS_NAME;
|
import static ca.uhn.fhir.jpa.model.util.JpaConstants.ALL_PARTITIONS_NAME;
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
@ -181,9 +177,18 @@ import static org.apache.commons.lang3.StringUtils.trim;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: JA - This class has only one subclass now. Historically it was a common
|
||||||
|
* ancestor for BaseHapiFhirSystemDao and BaseHapiFhirResourceDao but I've untangled
|
||||||
|
* the former from this hierarchy in order to simplify moving common functionality
|
||||||
|
* for resource DAOs into the hapi-fhir-storage project. This class should be merged
|
||||||
|
* into BaseHapiFhirResourceDao, but that should be done in its own dedicated PR
|
||||||
|
* since it'll be a noisy change.
|
||||||
|
*/
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
@Repository
|
@Repository
|
||||||
public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStorageDao implements IDao, IJpaDao<T>, ApplicationContextAware {
|
public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStorageResourceDao<T> implements IDao, IJpaDao<T>, ApplicationContextAware {
|
||||||
|
|
||||||
public static final long INDEX_STATUS_INDEXED = 1L;
|
public static final long INDEX_STATUS_INDEXED = 1L;
|
||||||
public static final long INDEX_STATUS_INDEXING_FAILED = 2L;
|
public static final long INDEX_STATUS_INDEXING_FAILED = 2L;
|
||||||
|
@ -233,8 +238,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
@Autowired
|
@Autowired
|
||||||
private PartitionSettings myPartitionSettings;
|
private PartitionSettings myPartitionSettings;
|
||||||
@Autowired
|
@Autowired
|
||||||
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
|
||||||
@Autowired
|
|
||||||
private IPartitionLookupSvc myPartitionLookupSvc;
|
private IPartitionLookupSvc myPartitionLookupSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MemoryCacheService myMemoryCacheService;
|
private MemoryCacheService myMemoryCacheService;
|
||||||
|
@ -243,6 +246,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlatformTransactionManager myTransactionManager;
|
private PlatformTransactionManager myTransactionManager;
|
||||||
|
@Autowired
|
||||||
|
protected IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void setSearchParamPresenceSvc(ISearchParamPresenceSvc theSearchParamPresenceSvc) {
|
public void setSearchParamPresenceSvc(ISearchParamPresenceSvc theSearchParamPresenceSvc) {
|
||||||
|
@ -371,14 +376,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
myContext = theContext;
|
myContext = theContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FhirContext getContext(FhirVersionEnum theVersion) {
|
|
||||||
Validate.notNull(theVersion, "theVersion must not be null");
|
|
||||||
if (theVersion == myFhirContext.getVersion().getVersion()) {
|
|
||||||
return myFhirContext;
|
|
||||||
}
|
|
||||||
return FhirContext.forCached(theVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>null</code> will only be returned if the scheme and tag are both blank
|
* <code>null</code> will only be returned if the scheme and tag are both blank
|
||||||
*/
|
*/
|
||||||
|
@ -513,27 +510,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IBundleProvider history(RequestDetails theRequest, String theResourceType, Long theResourcePid, Date theRangeStartInclusive, Date theRangeEndInclusive, Integer theOffset) {
|
|
||||||
return history(theRequest, theResourceType, theResourcePid, theRangeStartInclusive, theRangeEndInclusive, theOffset, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IBundleProvider history(RequestDetails theRequest, String theResourceType, Long theResourcePid, Date theRangeStartInclusive, Date theRangeEndInclusive, Integer theOffset, HistorySearchStyleEnum searchParameterType) {
|
|
||||||
String resourceName = defaultIfBlank(theResourceType, null);
|
|
||||||
|
|
||||||
Search search = new Search();
|
|
||||||
search.setOffset(theOffset);
|
|
||||||
search.setDeleted(false);
|
|
||||||
search.setCreated(new Date());
|
|
||||||
search.setLastUpdated(theRangeStartInclusive, theRangeEndInclusive);
|
|
||||||
search.setUuid(UUID.randomUUID().toString());
|
|
||||||
search.setResourceType(resourceName);
|
|
||||||
search.setResourceId(theResourcePid);
|
|
||||||
search.setSearchType(SearchTypeEnum.HISTORY);
|
|
||||||
search.setStatus(SearchStatusEnum.FINISHED);
|
|
||||||
search.setHistorySearchStyle(searchParameterType);
|
|
||||||
|
|
||||||
return myPersistedJpaBundleProviderFactory.newInstance(theRequest, search);
|
|
||||||
}
|
|
||||||
|
|
||||||
void incrementId(T theResource, ResourceTable theSavedEntity, IIdType theResourceId) {
|
void incrementId(T theResource, ResourceTable theSavedEntity, IIdType theResourceId) {
|
||||||
String newVersion;
|
String newVersion;
|
||||||
|
@ -796,133 +772,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
return !allTagsOld.equals(allTagsNew);
|
return !allTagsOld.equals(allTagsNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <R extends IBaseResource> R populateResourceMetadataHapi(Class<R> theResourceType, IBaseResourceEntity theEntity, @Nullable Collection<? extends BaseTag> theTagList, boolean theForHistoryOperation, IResource res, Long theVersion) {
|
|
||||||
R retVal = (R) res;
|
|
||||||
if (theEntity.getDeleted() != null) {
|
|
||||||
res = (IResource) myContext.getResourceDefinition(theResourceType).newInstance();
|
|
||||||
retVal = (R) res;
|
|
||||||
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
|
|
||||||
if (theForHistoryOperation) {
|
|
||||||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE);
|
|
||||||
}
|
|
||||||
} else if (theForHistoryOperation) {
|
|
||||||
/*
|
|
||||||
* If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
|
|
||||||
*/
|
|
||||||
Date published = theEntity.getPublished().getValue();
|
|
||||||
Date updated = theEntity.getUpdated().getValue();
|
|
||||||
if (published.equals(updated)) {
|
|
||||||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST);
|
|
||||||
} else {
|
|
||||||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.setId(theEntity.getIdDt().withVersion(theVersion.toString()));
|
|
||||||
|
|
||||||
ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion()));
|
|
||||||
ResourceMetadataKeyEnum.PUBLISHED.put(res, theEntity.getPublished());
|
|
||||||
ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated());
|
|
||||||
IDao.RESOURCE_PID.put(res, theEntity.getResourceId());
|
|
||||||
|
|
||||||
if (theTagList != null) {
|
|
||||||
if (theEntity.isHasTags()) {
|
|
||||||
TagList tagList = new TagList();
|
|
||||||
List<IBaseCoding> securityLabels = new ArrayList<>();
|
|
||||||
List<IdDt> profiles = new ArrayList<>();
|
|
||||||
for (BaseTag next : theTagList) {
|
|
||||||
switch (next.getTag().getTagType()) {
|
|
||||||
case PROFILE:
|
|
||||||
profiles.add(new IdDt(next.getTag().getCode()));
|
|
||||||
break;
|
|
||||||
case SECURITY_LABEL:
|
|
||||||
IBaseCoding secLabel = (IBaseCoding) myContext.getVersion().newCodingDt();
|
|
||||||
secLabel.setSystem(next.getTag().getSystem());
|
|
||||||
secLabel.setCode(next.getTag().getCode());
|
|
||||||
secLabel.setDisplay(next.getTag().getDisplay());
|
|
||||||
securityLabels.add(secLabel);
|
|
||||||
break;
|
|
||||||
case TAG:
|
|
||||||
tagList.add(new Tag(next.getTag().getSystem(), next.getTag().getCode(), next.getTag().getDisplay()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tagList.size() > 0) {
|
|
||||||
ResourceMetadataKeyEnum.TAG_LIST.put(res, tagList);
|
|
||||||
}
|
|
||||||
if (securityLabels.size() > 0) {
|
|
||||||
ResourceMetadataKeyEnum.SECURITY_LABELS.put(res, toBaseCodingList(securityLabels));
|
|
||||||
}
|
|
||||||
if (profiles.size() > 0) {
|
|
||||||
ResourceMetadataKeyEnum.PROFILES.put(res, profiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <R extends IBaseResource> R populateResourceMetadataRi(Class<R> theResourceType, IBaseResourceEntity theEntity, @Nullable Collection<? extends BaseTag> theTagList, boolean theForHistoryOperation, IAnyResource res, Long theVersion) {
|
|
||||||
R retVal = (R) res;
|
|
||||||
if (theEntity.getDeleted() != null) {
|
|
||||||
res = (IAnyResource) myContext.getResourceDefinition(theResourceType).newInstance();
|
|
||||||
retVal = (R) res;
|
|
||||||
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
|
|
||||||
if (theForHistoryOperation) {
|
|
||||||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, HTTPVerb.DELETE.toCode());
|
|
||||||
}
|
|
||||||
} else if (theForHistoryOperation) {
|
|
||||||
/*
|
|
||||||
* If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
|
|
||||||
*/
|
|
||||||
Date published = theEntity.getPublished().getValue();
|
|
||||||
Date updated = theEntity.getUpdated().getValue();
|
|
||||||
if (published.equals(updated)) {
|
|
||||||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, HTTPVerb.POST.toCode());
|
|
||||||
} else {
|
|
||||||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, HTTPVerb.PUT.toCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.getMeta().setLastUpdated(null);
|
|
||||||
res.getMeta().setVersionId(null);
|
|
||||||
|
|
||||||
updateResourceMetadata(theEntity, res);
|
|
||||||
res.setId(res.getIdElement().withVersion(theVersion.toString()));
|
|
||||||
|
|
||||||
res.getMeta().setLastUpdated(theEntity.getUpdatedDate());
|
|
||||||
IDao.RESOURCE_PID.put(res, theEntity.getResourceId());
|
|
||||||
|
|
||||||
if (theTagList != null) {
|
|
||||||
res.getMeta().getTag().clear();
|
|
||||||
res.getMeta().getProfile().clear();
|
|
||||||
res.getMeta().getSecurity().clear();
|
|
||||||
for (BaseTag next : theTagList) {
|
|
||||||
switch (next.getTag().getTagType()) {
|
|
||||||
case PROFILE:
|
|
||||||
res.getMeta().addProfile(next.getTag().getCode());
|
|
||||||
break;
|
|
||||||
case SECURITY_LABEL:
|
|
||||||
IBaseCoding sec = res.getMeta().addSecurity();
|
|
||||||
sec.setSystem(next.getTag().getSystem());
|
|
||||||
sec.setCode(next.getTag().getCode());
|
|
||||||
sec.setDisplay(next.getTag().getDisplay());
|
|
||||||
break;
|
|
||||||
case TAG:
|
|
||||||
IBaseCoding tag = res.getMeta().addTag();
|
|
||||||
tag.setSystem(next.getTag().getSystem());
|
|
||||||
tag.setCode(next.getTag().getCode());
|
|
||||||
tag.setDisplay(next.getTag().getDisplay());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
|
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
|
||||||
*
|
*
|
||||||
|
@ -954,6 +803,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@CoverageIgnore
|
@CoverageIgnore
|
||||||
public BaseHasResource readEntity(IIdType theValueId, RequestDetails theRequest) {
|
public BaseHasResource readEntity(IIdType theValueId, RequestDetails theRequest) {
|
||||||
throw new NotImplementedException(Msg.code(927) + "");
|
throw new NotImplementedException(Msg.code(927) + "");
|
||||||
|
@ -1005,220 +855,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
return metaSnapshotModeTokens.contains(theTag.getTag().getTagType());
|
return metaSnapshotModeTokens.contains(theTag.getTag().getTagType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
|
|
||||||
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
|
||||||
Class<? extends IBaseResource> resourceType = type.getImplementingClass();
|
|
||||||
return toResource(resourceType, theEntity, null, theForHistoryOperation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public <R extends IBaseResource> R toResource(Class<R> theResourceType, IBaseResourceEntity theEntity, Collection<ResourceTag> theTagList, boolean theForHistoryOperation) {
|
|
||||||
|
|
||||||
// 1. get resource, it's encoding and the tags if any
|
|
||||||
byte[] resourceBytes;
|
|
||||||
String resourceText;
|
|
||||||
ResourceEncodingEnum resourceEncoding;
|
|
||||||
@Nullable
|
|
||||||
Collection<? extends BaseTag> tagList = Collections.emptyList();
|
|
||||||
long version;
|
|
||||||
String provenanceSourceUri = null;
|
|
||||||
String provenanceRequestId = null;
|
|
||||||
|
|
||||||
if (theEntity instanceof ResourceHistoryTable) {
|
|
||||||
ResourceHistoryTable history = (ResourceHistoryTable) theEntity;
|
|
||||||
resourceBytes = history.getResource();
|
|
||||||
resourceText = history.getResourceTextVc();
|
|
||||||
resourceEncoding = history.getEncoding();
|
|
||||||
switch (getConfig().getTagStorageMode()) {
|
|
||||||
case VERSIONED:
|
|
||||||
default:
|
|
||||||
if (history.isHasTags()) {
|
|
||||||
tagList = history.getTags();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NON_VERSIONED:
|
|
||||||
if (history.getResourceTable().isHasTags()) {
|
|
||||||
tagList = history.getResourceTable().getTags();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INLINE:
|
|
||||||
tagList = null;
|
|
||||||
}
|
|
||||||
version = history.getVersion();
|
|
||||||
if (history.getProvenance() != null) {
|
|
||||||
provenanceRequestId = history.getProvenance().getRequestId();
|
|
||||||
provenanceSourceUri = history.getProvenance().getSourceUri();
|
|
||||||
}
|
|
||||||
} else if (theEntity instanceof ResourceTable) {
|
|
||||||
ResourceTable resource = (ResourceTable) theEntity;
|
|
||||||
ResourceHistoryTable history;
|
|
||||||
if (resource.getCurrentVersionEntity() != null) {
|
|
||||||
history = resource.getCurrentVersionEntity();
|
|
||||||
} else {
|
|
||||||
version = theEntity.getVersion();
|
|
||||||
history = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(theEntity.getId(), version);
|
|
||||||
((ResourceTable) theEntity).setCurrentVersionEntity(history);
|
|
||||||
|
|
||||||
while (history == null) {
|
|
||||||
if (version > 1L) {
|
|
||||||
version--;
|
|
||||||
history = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(theEntity.getId(), version);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceBytes = history.getResource();
|
|
||||||
resourceEncoding = history.getEncoding();
|
|
||||||
resourceText = history.getResourceTextVc();
|
|
||||||
switch (getConfig().getTagStorageMode()) {
|
|
||||||
case VERSIONED:
|
|
||||||
case NON_VERSIONED:
|
|
||||||
if (resource.isHasTags()) {
|
|
||||||
tagList = resource.getTags();
|
|
||||||
} else {
|
|
||||||
tagList = Collections.emptyList();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INLINE:
|
|
||||||
tagList = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
version = history.getVersion();
|
|
||||||
if (history.getProvenance() != null) {
|
|
||||||
provenanceRequestId = history.getProvenance().getRequestId();
|
|
||||||
provenanceSourceUri = history.getProvenance().getSourceUri();
|
|
||||||
}
|
|
||||||
} else if (theEntity instanceof ResourceSearchView) {
|
|
||||||
// This is the search View
|
|
||||||
ResourceSearchView view = (ResourceSearchView) theEntity;
|
|
||||||
resourceBytes = view.getResource();
|
|
||||||
resourceText = view.getResourceTextVc();
|
|
||||||
resourceEncoding = view.getEncoding();
|
|
||||||
version = view.getVersion();
|
|
||||||
provenanceRequestId = view.getProvenanceRequestId();
|
|
||||||
provenanceSourceUri = view.getProvenanceSourceUri();
|
|
||||||
switch (getConfig().getTagStorageMode()) {
|
|
||||||
case VERSIONED:
|
|
||||||
case NON_VERSIONED:
|
|
||||||
if (theTagList != null) {
|
|
||||||
tagList = theTagList;
|
|
||||||
} else {
|
|
||||||
tagList = Collections.emptyList();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case INLINE:
|
|
||||||
tagList = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// something wrong
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. get The text
|
|
||||||
String decodedResourceText;
|
|
||||||
if (resourceText != null) {
|
|
||||||
decodedResourceText = resourceText;
|
|
||||||
} else {
|
|
||||||
decodedResourceText = decodeResource(resourceBytes, resourceEncoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Use the appropriate custom type if one is specified in the context
|
|
||||||
Class<R> resourceType = theResourceType;
|
|
||||||
if (tagList != null) {
|
|
||||||
if (myContext.hasDefaultTypeForProfile()) {
|
|
||||||
for (BaseTag nextTag : tagList) {
|
|
||||||
if (nextTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
|
|
||||||
String profile = nextTag.getTag().getCode();
|
|
||||||
if (isNotBlank(profile)) {
|
|
||||||
Class<? extends IBaseResource> newType = myContext.getDefaultTypeForProfile(profile);
|
|
||||||
if (newType != null && theResourceType.isAssignableFrom(newType)) {
|
|
||||||
ourLog.debug("Using custom type {} for profile: {}", newType.getName(), profile);
|
|
||||||
resourceType = (Class<R>) newType;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. parse the text to FHIR
|
|
||||||
R retVal;
|
|
||||||
if (resourceEncoding != ResourceEncodingEnum.DEL) {
|
|
||||||
|
|
||||||
LenientErrorHandler errorHandler = new LenientErrorHandler(false).setErrorOnInvalidValue(false);
|
|
||||||
IParser parser = new TolerantJsonParser(getContext(theEntity.getFhirVersion()), errorHandler, theEntity.getId());
|
|
||||||
|
|
||||||
try {
|
|
||||||
retVal = parser.parseResource(resourceType, decodedResourceText);
|
|
||||||
} catch (Exception e) {
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
b.append("Failed to parse database resource[");
|
|
||||||
b.append(myFhirContext.getResourceType(resourceType));
|
|
||||||
b.append("/");
|
|
||||||
b.append(theEntity.getIdDt().getIdPart());
|
|
||||||
b.append(" (pid ");
|
|
||||||
b.append(theEntity.getId());
|
|
||||||
b.append(", version ");
|
|
||||||
b.append(theEntity.getFhirVersion().name());
|
|
||||||
b.append("): ");
|
|
||||||
b.append(e.getMessage());
|
|
||||||
String msg = b.toString();
|
|
||||||
ourLog.error(msg, e);
|
|
||||||
throw new DataFormatException(Msg.code(928) + msg, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
retVal = (R) myContext.getResourceDefinition(theEntity.getResourceType()).newInstance();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. fill MetaData
|
|
||||||
retVal = populateResourceMetadata(theEntity, theForHistoryOperation, tagList, version, resourceType, retVal);
|
|
||||||
|
|
||||||
// 6. Handle source (provenance)
|
|
||||||
if (isNotBlank(provenanceRequestId) || isNotBlank(provenanceSourceUri)) {
|
|
||||||
String sourceString = cleanProvenanceSourceUri(provenanceSourceUri)
|
|
||||||
+ (isNotBlank(provenanceRequestId) ? "#" : "")
|
|
||||||
+ defaultString(provenanceRequestId);
|
|
||||||
|
|
||||||
MetaUtil.setSource(myContext, retVal, sourceString);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Add partition information
|
|
||||||
if (myPartitionSettings.isPartitioningEnabled()) {
|
|
||||||
PartitionablePartitionId partitionId = theEntity.getPartitionId();
|
|
||||||
if (partitionId != null && partitionId.getPartitionId() != null) {
|
|
||||||
PartitionEntity persistedPartition = myPartitionLookupSvc.getPartitionById(partitionId.getPartitionId());
|
|
||||||
retVal.setUserData(Constants.RESOURCE_PARTITION_ID, persistedPartition.toRequestPartitionId());
|
|
||||||
} else {
|
|
||||||
retVal.setUserData(Constants.RESOURCE_PARTITION_ID, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <R extends IBaseResource> R populateResourceMetadata(IBaseResourceEntity theEntity, boolean theForHistoryOperation, @Nullable Collection<? extends BaseTag> tagList, long theVersion, Class<R> theResourceType, R theResource) {
|
|
||||||
if (theResource instanceof IResource) {
|
|
||||||
IResource res = (IResource) theResource;
|
|
||||||
theResource = populateResourceMetadataHapi(theResourceType, theEntity, tagList, theForHistoryOperation, res, theVersion);
|
|
||||||
} else {
|
|
||||||
IAnyResource res = (IAnyResource) theResource;
|
|
||||||
theResource = populateResourceMetadataRi(theResourceType, theEntity, tagList, theForHistoryOperation, res, theVersion);
|
|
||||||
}
|
|
||||||
return theResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toResourceName(Class<? extends IBaseResource> theResourceType) {
|
|
||||||
return myContext.getResourceType(theResourceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
String toResourceName(IBaseResource theResource) {
|
String toResourceName(IBaseResource theResource) {
|
||||||
return myContext.getResourceType(theResource);
|
return myContext.getResourceType(theResource);
|
||||||
|
@ -1375,7 +1013,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
if (thePerformIndexing && changed != null && !changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange() && (entity.getVersion() > 1 || theUpdateVersion)) {
|
if (thePerformIndexing && changed != null && !changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange() && (entity.getVersion() > 1 || theUpdateVersion)) {
|
||||||
ourLog.debug("Resource {} has not changed", entity.getIdDt().toUnqualified().getValue());
|
ourLog.debug("Resource {} has not changed", entity.getIdDt().toUnqualified().getValue());
|
||||||
if (theResource != null) {
|
if (theResource != null) {
|
||||||
updateResourceMetadata(entity, theResource);
|
myJpaStorageResourceParser.updateResourceMetadata(entity, theResource);
|
||||||
}
|
}
|
||||||
entity.setUnchangedInCurrentOperation(true);
|
entity.setUnchangedInCurrentOperation(true);
|
||||||
return entity;
|
return entity;
|
||||||
|
@ -1475,7 +1113,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theResource != null) {
|
if (theResource != null) {
|
||||||
updateResourceMetadata(entity, theResource);
|
myJpaStorageResourceParser.updateResourceMetadata(entity, theResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1498,7 +1136,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
if (getConfig().isMassIngestionMode()) {
|
if (getConfig().isMassIngestionMode()) {
|
||||||
oldResource = null;
|
oldResource = null;
|
||||||
} else {
|
} else {
|
||||||
oldResource = toResource(entity, false);
|
oldResource = myJpaStorageResourceParser.toResource(entity, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyInterceptors(theRequest, theResource, oldResource, theTransactionDetails, true);
|
notifyInterceptors(theRequest, theResource, oldResource, theTransactionDetails, true);
|
||||||
|
@ -1510,7 +1148,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
historyEntity = ((ResourceTable) readEntity(theResourceId, theRequest)).getCurrentVersionEntity();
|
historyEntity = ((ResourceTable) readEntity(theResourceId, theRequest)).getCurrentVersionEntity();
|
||||||
|
|
||||||
// Update version/lastUpdated so that interceptors see the correct version
|
// Update version/lastUpdated so that interceptors see the correct version
|
||||||
updateResourceMetadata(savedEntity, theResource);
|
myJpaStorageResourceParser.updateResourceMetadata(savedEntity, theResource);
|
||||||
// Populate the PID in the resource, so it is available to hooks
|
// Populate the PID in the resource, so it is available to hooks
|
||||||
addPidToResource(savedEntity, theResource);
|
addPidToResource(savedEntity, theResource);
|
||||||
|
|
||||||
|
@ -1537,7 +1175,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
|
|
||||||
if (!changed && myConfig.isSuppressUpdatesWithNoChange() && (historyEntity.getVersion() > 1)) {
|
if (!changed && myConfig.isSuppressUpdatesWithNoChange() && (historyEntity.getVersion() > 1)) {
|
||||||
ourLog.debug("Resource {} has not changed", historyEntity.getIdDt().toUnqualified().getValue());
|
ourLog.debug("Resource {} has not changed", historyEntity.getIdDt().toUnqualified().getValue());
|
||||||
updateResourceMetadata(historyEntity, theResource);
|
myJpaStorageResourceParser.updateResourceMetadata(historyEntity, theResource);
|
||||||
return historyEntity;
|
return historyEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1556,7 +1194,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
historyEntity.setResourceTextVc(encodedResource.getResourceText());
|
historyEntity.setResourceTextVc(encodedResource.getResourceText());
|
||||||
myResourceHistoryTableDao.save(historyEntity);
|
myResourceHistoryTableDao.save(historyEntity);
|
||||||
|
|
||||||
updateResourceMetadata(historyEntity, theResource);
|
myJpaStorageResourceParser.updateResourceMetadata(historyEntity, theResource);
|
||||||
|
|
||||||
return historyEntity;
|
return historyEntity;
|
||||||
}
|
}
|
||||||
|
@ -1586,14 +1224,15 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO eventually consider refactoring this to be part of an interceptor.
|
* TODO eventually consider refactoring this to be part of an interceptor.
|
||||||
*
|
* <p>
|
||||||
* Throws an exception if the partition of the request, and the partition of the existing entity do not match.
|
* Throws an exception if the partition of the request, and the partition of the existing entity do not match.
|
||||||
|
*
|
||||||
* @param theRequest the request.
|
* @param theRequest the request.
|
||||||
* @param entity the existing entity.
|
* @param entity the existing entity.
|
||||||
*/
|
*/
|
||||||
private void failIfPartitionMismatch(RequestDetails theRequest, ResourceTable entity) {
|
private void failIfPartitionMismatch(RequestDetails theRequest, ResourceTable entity) {
|
||||||
if (myPartitionSettings.isPartitioningEnabled() && theRequest != null && theRequest.getTenantId() != null && entity.getPartitionId() != null &&
|
if (myPartitionSettings.isPartitioningEnabled() && theRequest != null && theRequest.getTenantId() != null && entity.getPartitionId() != null &&
|
||||||
theRequest.getTenantId() != ALL_PARTITIONS_NAME) {
|
!ALL_PARTITIONS_NAME.equals(theRequest.getTenantId())) {
|
||||||
PartitionEntity partitionEntity = myPartitionLookupSvc.getPartitionByName(theRequest.getTenantId());
|
PartitionEntity partitionEntity = myPartitionLookupSvc.getPartitionByName(theRequest.getTenantId());
|
||||||
//partitionEntity should never be null
|
//partitionEntity should never be null
|
||||||
if (partitionEntity != null && !partitionEntity.getId().equals(entity.getPartitionId().getPartitionId())) {
|
if (partitionEntity != null && !partitionEntity.getId().equals(entity.getPartitionId().getPartitionId())) {
|
||||||
|
@ -1668,8 +1307,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResourceTable updateInternal(RequestDetails theRequestDetails, T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion,
|
public DaoMethodOutcome updateInternal(RequestDetails theRequestDetails, T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion,
|
||||||
IBasePersistedResource theEntity, IIdType theResourceId, IBaseResource theOldResource, TransactionDetails theTransactionDetails) {
|
IBasePersistedResource theEntity, IIdType theResourceId, @Nullable IBaseResource theOldResource, RestOperationTypeEnum theOperationType, TransactionDetails theTransactionDetails) {
|
||||||
|
|
||||||
ResourceTable entity = (ResourceTable) theEntity;
|
ResourceTable entity = (ResourceTable) theEntity;
|
||||||
|
|
||||||
|
@ -1696,7 +1335,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update version/lastUpdated so that interceptors see the correct version
|
// Update version/lastUpdated so that interceptors see the correct version
|
||||||
updateResourceMetadata(savedEntity, theResource);
|
myJpaStorageResourceParser.updateResourceMetadata(savedEntity, theResource);
|
||||||
|
|
||||||
// Populate the PID in the resource so it is available to hooks
|
// Populate the PID in the resource so it is available to hooks
|
||||||
addPidToResource(savedEntity, theResource);
|
addPidToResource(savedEntity, theResource);
|
||||||
|
@ -1706,7 +1345,42 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
notifyInterceptors(theRequestDetails, theResource, theOldResource, theTransactionDetails, false);
|
notifyInterceptors(theRequestDetails, theResource, theOldResource, theTransactionDetails, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return savedEntity;
|
Collection<? extends BaseTag> tagList = Collections.emptyList();
|
||||||
|
if (entity.isHasTags()) {
|
||||||
|
tagList = entity.getTags();
|
||||||
|
}
|
||||||
|
long version = entity.getVersion();
|
||||||
|
myJpaStorageResourceParser.populateResourceMetadata(entity, false, tagList, version, theResource);
|
||||||
|
|
||||||
|
boolean wasDeleted = false;
|
||||||
|
// NB If this if-else ever gets collapsed, make sure to account for possible null (will happen in mass-ingestion mode)
|
||||||
|
if (theOldResource instanceof IResource) {
|
||||||
|
wasDeleted = ResourceMetadataKeyEnum.DELETED_AT.get((IResource) theOldResource) != null;
|
||||||
|
} else if (theOldResource instanceof IAnyResource) {
|
||||||
|
wasDeleted = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) theOldResource) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DaoMethodOutcome outcome = toMethodOutcome(theRequestDetails, savedEntity, theResource, theMatchUrl, theOperationType).setCreated(wasDeleted);
|
||||||
|
|
||||||
|
if (!thePerformIndexing) {
|
||||||
|
IIdType id = getContext().getVersion().newIdType();
|
||||||
|
id.setValue(entity.getIdDt().getValue());
|
||||||
|
outcome.setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only include a task timer if we're not in a sub-request (i.e. a transaction)
|
||||||
|
// since individual item times don't actually make much sense in the context
|
||||||
|
// of a transaction
|
||||||
|
StopWatch w = null;
|
||||||
|
if (theRequestDetails != null && !theRequestDetails.isSubRequest()) {
|
||||||
|
if (theTransactionDetails != null && !theTransactionDetails.isFhirTransaction()) {
|
||||||
|
w = new StopWatch(theTransactionDetails.getTransactionDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
populateOperationOutcomeForUpdate(w, outcome, theMatchUrl, outcome.getOperationType());
|
||||||
|
|
||||||
|
return outcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyInterceptors(RequestDetails theRequestDetails, T theResource, IBaseResource theOldResource, TransactionDetails theTransactionDetails, boolean isUnchanged) {
|
private void notifyInterceptors(RequestDetails theRequestDetails, T theResource, IBaseResource theOldResource, TransactionDetails theTransactionDetails, boolean isUnchanged) {
|
||||||
|
@ -1735,26 +1409,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateResourceMetadata(IBaseResourceEntity theEntity, IBaseResource theResource) {
|
|
||||||
IIdType id = theEntity.getIdDt();
|
|
||||||
if (getContext().getVersion().getVersion().isRi()) {
|
|
||||||
id = getContext().getVersion().newIdType().setValue(id.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id.hasResourceType() == false) {
|
|
||||||
id = id.withResourceType(theEntity.getResourceType());
|
|
||||||
}
|
|
||||||
|
|
||||||
theResource.setId(id);
|
|
||||||
if (theResource instanceof IResource) {
|
|
||||||
ResourceMetadataKeyEnum.VERSION.put((IResource) theResource, id.getVersionIdPart());
|
|
||||||
ResourceMetadataKeyEnum.UPDATED.put((IResource) theResource, theEntity.getUpdated());
|
|
||||||
} else {
|
|
||||||
IBaseMetaType meta = theResource.getMeta();
|
|
||||||
meta.setVersionId(id.getVersionIdPart());
|
|
||||||
meta.setLastUpdated(theEntity.getUpdatedDate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateChildReferenceTargetTypes(IBase theElement, String thePath) {
|
private void validateChildReferenceTargetTypes(IBase theElement, String thePath) {
|
||||||
if (theElement == null) {
|
if (theElement == null) {
|
||||||
|
@ -1896,6 +1551,22 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
myPartitionSettings = thePartitionSettings;
|
myPartitionSettings = thePartitionSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class AddTagDefinitionToCacheAfterCommitSynchronization implements TransactionSynchronization {
|
||||||
|
|
||||||
|
private final TagDefinition myTagDefinition;
|
||||||
|
private final MemoryCacheService.TagDefinitionCacheKey myKey;
|
||||||
|
|
||||||
|
public AddTagDefinitionToCacheAfterCommitSynchronization(MemoryCacheService.TagDefinitionCacheKey theKey, TagDefinition theTagDefinition) {
|
||||||
|
myTagDefinition = theTagDefinition;
|
||||||
|
myKey = theKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCommit() {
|
||||||
|
myMemoryCacheService.put(MemoryCacheService.CacheEnum.TAG_DEFINITION, myKey, myTagDefinition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static MemoryCacheService.TagDefinitionCacheKey toTagDefinitionMemoryCacheKey(TagTypeEnum theTagType, String theScheme, String theTerm) {
|
public static MemoryCacheService.TagDefinitionCacheKey toTagDefinitionMemoryCacheKey(TagTypeEnum theTagType, String theScheme, String theTerm) {
|
||||||
return new MemoryCacheService.TagDefinitionCacheKey(theTagType, theScheme, theTerm);
|
return new MemoryCacheService.TagDefinitionCacheKey(theTagType, theScheme, theTerm);
|
||||||
|
@ -1999,34 +1670,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
ourValidationDisabledForUnitTest = theValidationDisabledForUnitTest;
|
ourValidationDisabledForUnitTest = theValidationDisabledForUnitTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<BaseCodingDt> toBaseCodingList(List<IBaseCoding> theSecurityLabels) {
|
/**
|
||||||
ArrayList<BaseCodingDt> retVal = new ArrayList<>(theSecurityLabels.size());
|
* Do not call this method outside of unit tests
|
||||||
for (IBaseCoding next : theSecurityLabels) {
|
*/
|
||||||
retVal.add((BaseCodingDt) next);
|
@VisibleForTesting
|
||||||
}
|
public void setJpaStorageResourceParserForUnitTest(IJpaStorageResourceParser theJpaStorageResourceParser) {
|
||||||
return retVal;
|
myJpaStorageResourceParser = theJpaStorageResourceParser;
|
||||||
}
|
|
||||||
|
|
||||||
public static void validateResourceType(BaseHasResource theEntity, String theResourceName) {
|
|
||||||
if (!theResourceName.equals(theEntity.getResourceType())) {
|
|
||||||
throw new ResourceNotFoundException(Msg.code(935) + "Resource with ID " + theEntity.getIdDt().getIdPart() + " exists but it is not of type " + theResourceName + ", found resource of type " + theEntity.getResourceType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AddTagDefinitionToCacheAfterCommitSynchronization implements TransactionSynchronization {
|
|
||||||
|
|
||||||
private final TagDefinition myTagDefinition;
|
|
||||||
private final MemoryCacheService.TagDefinitionCacheKey myKey;
|
|
||||||
|
|
||||||
public AddTagDefinitionToCacheAfterCommitSynchronization(MemoryCacheService.TagDefinitionCacheKey theKey, TagDefinition theTagDefinition) {
|
|
||||||
myTagDefinition = theTagDefinition;
|
|
||||||
myKey = theKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterCommit() {
|
|
||||||
myMemoryCacheService.put(MemoryCacheService.CacheEnum.TAG_DEFINITION, myKey, myTagDefinition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,26 +58,23 @@ import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.patch.FhirPatch;
|
|
||||||
import ca.uhn.fhir.jpa.patch.JsonPatchUtils;
|
|
||||||
import ca.uhn.fhir.jpa.patch.XmlPatchUtils;
|
|
||||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
|
||||||
|
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory;
|
||||||
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
|
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
|
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.model.api.StorageResponseCodeEnum;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.ListResource;
|
import ca.uhn.fhir.model.dstu2.resource.ListResource;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.InterceptorInvocationTimingEnum;
|
import ca.uhn.fhir.rest.api.InterceptorInvocationTimingEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
|
import ca.uhn.fhir.rest.api.SearchContainedModeEnum;
|
||||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||||
|
@ -105,9 +102,9 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
||||||
import ca.uhn.fhir.util.ObjectUtil;
|
import ca.uhn.fhir.util.ObjectUtil;
|
||||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import ca.uhn.fhir.validation.FhirValidator;
|
import ca.uhn.fhir.validation.FhirValidator;
|
||||||
import ca.uhn.fhir.validation.IInstanceValidatorModule;
|
import ca.uhn.fhir.validation.IInstanceValidatorModule;
|
||||||
import ca.uhn.fhir.validation.IValidationContext;
|
import ca.uhn.fhir.validation.IValidationContext;
|
||||||
|
@ -119,7 +116,6 @@ import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
@ -161,7 +157,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
public static final String BASE_RESOURCE_NAME = "resource";
|
public static final String BASE_RESOURCE_NAME = "resource";
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected PlatformTransactionManager myPlatformTransactionManager;
|
protected PlatformTransactionManager myPlatformTransactionManager;
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
|
@ -182,18 +177,37 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
private IDeleteExpungeJobSubmitter myDeleteExpungeJobSubmitter;
|
private IDeleteExpungeJobSubmitter myDeleteExpungeJobSubmitter;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IJobCoordinator myJobCoordinator;
|
private IJobCoordinator myJobCoordinator;
|
||||||
|
|
||||||
private IInstanceValidatorModule myInstanceValidator;
|
private IInstanceValidatorModule myInstanceValidator;
|
||||||
private String myResourceName;
|
private String myResourceName;
|
||||||
private Class<T> myResourceType;
|
private Class<T> myResourceType;
|
||||||
|
@Autowired
|
||||||
|
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MemoryCacheService myMemoryCacheService;
|
private MemoryCacheService myMemoryCacheService;
|
||||||
private TransactionTemplate myTxTemplate;
|
private TransactionTemplate myTxTemplate;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UrlPartitioner myUrlPartitioner;
|
private UrlPartitioner myUrlPartitioner;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HapiTransactionService getTransactionService() {
|
||||||
|
return myTransactionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setTransactionService(HapiTransactionService theTransactionService) {
|
||||||
|
myTransactionService = theTransactionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MatchResourceUrlService getMatchResourceUrlService() {
|
||||||
|
return myMatchResourceUrlService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IStorageResourceParser getStorageResourceParser() {
|
||||||
|
return myJpaStorageResourceParser;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #create(T, RequestDetails)} instead
|
* @deprecated Use {@link #create(T, RequestDetails)} instead
|
||||||
*/
|
*/
|
||||||
|
@ -220,11 +234,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
return create(theResource, theIfNoneExist, true, new TransactionDetails(), theRequestDetails);
|
return create(theResource, theIfNoneExist, true, new TransactionDetails(), theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setTransactionService(HapiTransactionService theTransactionService) {
|
|
||||||
myTransactionService = theTransactionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
|
public DaoMethodOutcome create(T theResource, String theIfNoneExist, boolean thePerformIndexing, @Nonnull TransactionDetails theTransactionDetails, RequestDetails theRequestDetails) {
|
||||||
return myTransactionService.execute(theRequestDetails, theTransactionDetails, tx -> doCreateForPost(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails));
|
return myTransactionService.execute(theRequestDetails, theTransactionDetails, tx -> doCreateForPost(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails));
|
||||||
|
@ -260,14 +269,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineCreatePartitionForRequest(theRequestDetails, theResource, getResourceName());
|
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineCreatePartitionForRequest(theRequestDetails, theResource, getResourceName());
|
||||||
return doCreateForPostOrPut(theResource, theIfNoneExist, thePerformIndexing, theTransactionDetails, theRequestDetails, requestPartitionId);
|
return doCreateForPostOrPut(theRequestDetails, theResource, theIfNoneExist, true, thePerformIndexing, requestPartitionId, RestOperationTypeEnum.CREATE, theTransactionDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called both for FHIR create (POST) operations (via {@link #doCreateForPost(IBaseResource, String, boolean, TransactionDetails, RequestDetails)}
|
* Called both for FHIR create (POST) operations (via {@link #doCreateForPost(IBaseResource, String, boolean, TransactionDetails, RequestDetails)}
|
||||||
* as well as for FHIR update (PUT) where we're doing a create-with-client-assigned-ID (via {@link #doUpdate(IBaseResource, String, boolean, boolean, RequestDetails, TransactionDetails)}.
|
* as well as for FHIR update (PUT) where we're doing a create-with-client-assigned-ID (via {@link #doUpdate(IBaseResource, String, boolean, boolean, RequestDetails, TransactionDetails)}.
|
||||||
*/
|
*/
|
||||||
private DaoMethodOutcome doCreateForPostOrPut(T theResource, String theIfNoneExist, boolean thePerformIndexing, TransactionDetails theTransactionDetails, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) {
|
private DaoMethodOutcome doCreateForPostOrPut(RequestDetails theRequest, T theResource, String theMatchUrl, boolean theProcessMatchUrl, boolean thePerformIndexing, RequestPartitionId theRequestPartitionId, RestOperationTypeEnum theOperationType, TransactionDetails theTransactionDetails) {
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
|
|
||||||
preProcessResourceForStorage(theResource);
|
preProcessResourceForStorage(theResource);
|
||||||
|
@ -276,22 +285,21 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
ResourceTable entity = new ResourceTable();
|
ResourceTable entity = new ResourceTable();
|
||||||
entity.setResourceType(toResourceName(theResource));
|
entity.setResourceType(toResourceName(theResource));
|
||||||
entity.setPartitionId(myRequestPartitionHelperService.toStoragePartition(theRequestPartitionId));
|
entity.setPartitionId(myRequestPartitionHelperService.toStoragePartition(theRequestPartitionId));
|
||||||
entity.setCreatedByMatchUrl(theIfNoneExist);
|
entity.setCreatedByMatchUrl(theMatchUrl);
|
||||||
entity.setVersion(1);
|
entity.setVersion(1);
|
||||||
|
|
||||||
if (isNotBlank(theIfNoneExist)) {
|
if (isNotBlank(theMatchUrl) && theProcessMatchUrl) {
|
||||||
Set<JpaPid> match = myMatchResourceUrlService.processMatchUrl(theIfNoneExist, myResourceType, theTransactionDetails, theRequest)
|
Set<ResourcePersistentId> match = myMatchResourceUrlService.processMatchUrl(theMatchUrl, myResourceType, theTransactionDetails, theRequest);
|
||||||
.stream().map(id -> (JpaPid) id).collect(Collectors.toSet());
|
|
||||||
if (match.size() > 1) {
|
if (match.size() > 1) {
|
||||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "transactionOperationWithMultipleMatchFailure", "CREATE", theIfNoneExist, match.size());
|
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "transactionOperationWithMultipleMatchFailure", "CREATE", theMatchUrl, match.size());
|
||||||
throw new PreconditionFailedException(Msg.code(958) + msg);
|
throw new PreconditionFailedException(Msg.code(958) + msg);
|
||||||
} else if (match.size() == 1) {
|
} else if (match.size() == 1) {
|
||||||
JpaPid pid = match.iterator().next();
|
JpaPid pid = (JpaPid) match.iterator().next();
|
||||||
|
|
||||||
Supplier<LazyDaoMethodOutcome.EntityAndResource> entitySupplier = () -> {
|
Supplier<LazyDaoMethodOutcome.EntityAndResource> entitySupplier = () -> {
|
||||||
return myTxTemplate.execute(tx -> {
|
return myTxTemplate.execute(tx -> {
|
||||||
ResourceTable foundEntity = myEntityManager.find(ResourceTable.class, pid.getId());
|
ResourceTable foundEntity = myEntityManager.find(ResourceTable.class, pid.getId());
|
||||||
IBaseResource resource = toResource(foundEntity, false);
|
IBaseResource resource = myJpaStorageResourceParser.toResource(foundEntity, false);
|
||||||
theResource.setId(resource.getIdElement().getValue());
|
theResource.setId(resource.getIdElement().getValue());
|
||||||
return new LazyDaoMethodOutcome.EntityAndResource(foundEntity, resource);
|
return new LazyDaoMethodOutcome.EntityAndResource(foundEntity, resource);
|
||||||
});
|
});
|
||||||
|
@ -316,7 +324,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return toMethodOutcomeLazy(theRequest, pid, entitySupplier, idSupplier).setCreated(false).setNop(true);
|
DaoMethodOutcome outcome = toMethodOutcomeLazy(theRequest, pid, entitySupplier, idSupplier).setCreated(false).setNop(true);
|
||||||
|
StorageResponseCodeEnum responseCode = StorageResponseCodeEnum.SUCCESSFUL_CREATE_WITH_CONDITIONAL_MATCH;
|
||||||
|
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulCreateConditionalWithMatch", w.getMillisAndRestart(), UrlUtil.sanitizeUrlPart(theMatchUrl));
|
||||||
|
outcome.setOperationOutcome(createInfoOperationOutcome(msg, responseCode));
|
||||||
|
return outcome;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,15 +399,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
theTransactionDetails.addResolvedResourceId(jpaPid.getAssociatedResourceId(), jpaPid);
|
theTransactionDetails.addResolvedResourceId(jpaPid.getAssociatedResourceId(), jpaPid);
|
||||||
|
|
||||||
// Pre-cache the match URL
|
// Pre-cache the match URL
|
||||||
if (theIfNoneExist != null) {
|
if (theMatchUrl != null) {
|
||||||
myMatchResourceUrlService.matchUrlResolved(theTransactionDetails, getResourceName(), theIfNoneExist, jpaPid);
|
myMatchResourceUrlService.matchUrlResolved(theTransactionDetails, getResourceName(), theMatchUrl, jpaPid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the version/last updated in the resource so that interceptors get
|
// Update the version/last updated in the resource so that interceptors get
|
||||||
// the correct version
|
// the correct version
|
||||||
// TODO - the above updateEntity calls updateResourceMetadata
|
// TODO - the above updateEntity calls updateResourceMetadata
|
||||||
// Maybe we don't need this call here?
|
// Maybe we don't need this call here?
|
||||||
updateResourceMetadata(entity, theResource);
|
myJpaStorageResourceParser.updateResourceMetadata(entity, theResource);
|
||||||
|
|
||||||
// Populate the PID in the resource so it is available to hooks
|
// Populate the PID in the resource so it is available to hooks
|
||||||
addPidToResource(entity, theResource);
|
addPidToResource(entity, theResource);
|
||||||
|
@ -411,15 +423,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
doCallHooks(theTransactionDetails, theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, hookParams);
|
doCallHooks(theTransactionDetails, theRequest, Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED, hookParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
DaoMethodOutcome outcome = toMethodOutcome(theRequest, entity, theResource).setCreated(true);
|
DaoMethodOutcome outcome = toMethodOutcome(theRequest, entity, theResource, theMatchUrl, theOperationType)
|
||||||
|
.setCreated(true);
|
||||||
|
|
||||||
if (!thePerformIndexing) {
|
if (!thePerformIndexing) {
|
||||||
outcome.setId(theResource.getIdElement());
|
outcome.setId(theResource.getIdElement());
|
||||||
}
|
}
|
||||||
|
|
||||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart());
|
populateOperationOutcomeForUpdate(w, outcome, theMatchUrl, theOperationType);
|
||||||
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
|
|
||||||
|
|
||||||
ourLog.debug(msg);
|
|
||||||
return outcome;
|
return outcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,8 +545,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
// if not found, return an outcome anyways.
|
// if not found, return an outcome anyways.
|
||||||
// Because no object actually existed, we'll
|
// Because no object actually existed, we'll
|
||||||
// just set the id and nothing else
|
// just set the id and nothing else
|
||||||
DaoMethodOutcome outcome = createMethodOutcomeForResourceId(theId.getValue(), MESSAGE_KEY_DELETE_RESOURCE_NOT_EXISTING);
|
return createMethodOutcomeForResourceId(theId.getValue(), MESSAGE_KEY_DELETE_RESOURCE_NOT_EXISTING, StorageResponseCodeEnum.SUCCESSFUL_DELETE_NOT_FOUND);
|
||||||
return outcome;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theId.hasVersionIdPart() && Long.parseLong(theId.getVersionIdPart()) != entity.getVersion()) {
|
if (theId.hasVersionIdPart() && Long.parseLong(theId.getVersionIdPart()) != entity.getVersion()) {
|
||||||
|
@ -543,7 +554,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
// Don't delete again if it's already deleted
|
// Don't delete again if it's already deleted
|
||||||
if (isDeleted(entity)) {
|
if (isDeleted(entity)) {
|
||||||
DaoMethodOutcome outcome = createMethodOutcomeForResourceId(entity.getIdDt().getValue(), MESSAGE_KEY_DELETE_RESOURCE_ALREADY_DELETED);
|
DaoMethodOutcome outcome = createMethodOutcomeForResourceId(entity.getIdDt().getValue(), MESSAGE_KEY_DELETE_RESOURCE_ALREADY_DELETED, StorageResponseCodeEnum.SUCCESSFUL_DELETE_ALREADY_DELETED);
|
||||||
|
|
||||||
// used to exist, so we'll set the persistent id
|
// used to exist, so we'll set the persistent id
|
||||||
outcome.setPersistentId(new JpaPid(entity.getResourceId()));
|
outcome.setPersistentId(new JpaPid(entity.getResourceId()));
|
||||||
|
@ -554,7 +565,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
|
|
||||||
T resourceToDelete = toResource(myResourceType, entity, null, false);
|
T resourceToDelete = myJpaStorageResourceParser.toResource(myResourceType, entity, null, false);
|
||||||
theDeleteConflicts.setResourceIdMarkedForDeletion(theId);
|
theDeleteConflicts.setResourceIdMarkedForDeletion(theId);
|
||||||
|
|
||||||
// Notify IServerOperationInterceptors about pre-action call
|
// Notify IServerOperationInterceptors about pre-action call
|
||||||
|
@ -583,14 +594,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
doCallHooks(theTransactionDetails, theRequestDetails, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
|
doCallHooks(theTransactionDetails, theRequestDetails, Pointcut.STORAGE_PRECOMMIT_RESOURCE_DELETED, hookParams);
|
||||||
|
|
||||||
DaoMethodOutcome outcome = toMethodOutcome(theRequestDetails, savedEntity, resourceToDelete).setCreated(true);
|
DaoMethodOutcome outcome = toMethodOutcome(theRequestDetails, savedEntity, resourceToDelete, null, RestOperationTypeEnum.DELETE).setCreated(true);
|
||||||
|
|
||||||
IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(getContext());
|
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulDeletes", 1);
|
||||||
String message = getContext().getLocalizer().getMessage(BaseStorageDao.class, "successfulDeletes", 1, w.getMillis());
|
msg += " " + getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulTimingSuffix", w.getMillis());
|
||||||
String severity = "information";
|
outcome.setOperationOutcome(createInfoOperationOutcome(msg, StorageResponseCodeEnum.SUCCESSFUL_DELETE));
|
||||||
String code = "informational";
|
|
||||||
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
|
|
||||||
outcome.setOperationOutcome(oo);
|
|
||||||
|
|
||||||
return outcome;
|
return outcome;
|
||||||
}
|
}
|
||||||
|
@ -671,7 +679,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
ResourceTable entity = myEntityManager.find(ResourceTable.class, ((JpaPid) pid).getId());
|
ResourceTable entity = myEntityManager.find(ResourceTable.class, ((JpaPid) pid).getId());
|
||||||
deletedResources.add(entity);
|
deletedResources.add(entity);
|
||||||
|
|
||||||
T resourceToDelete = toResource(myResourceType, entity, null, false);
|
T resourceToDelete = myJpaStorageResourceParser.toResource(myResourceType, entity, null, false);
|
||||||
|
|
||||||
// Notify IServerOperationInterceptors about pre-action call
|
// Notify IServerOperationInterceptors about pre-action call
|
||||||
HookParams hooks = new HookParams()
|
HookParams hooks = new HookParams()
|
||||||
|
@ -705,17 +713,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
IBaseOperationOutcome oo;
|
IBaseOperationOutcome oo;
|
||||||
if (deletedResources.isEmpty()) {
|
if (deletedResources.isEmpty()) {
|
||||||
oo = OperationOutcomeUtil.newInstance(getContext());
|
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "unableToDeleteNotFound", theUrl);
|
||||||
String message = getMessageSanitized("unableToDeleteNotFound", theUrl);
|
oo = createOperationOutcome(OO_SEVERITY_WARN, msg, "not-found", StorageResponseCodeEnum.SUCCESSFUL_DELETE_NOT_FOUND);
|
||||||
String severity = "warning";
|
|
||||||
String code = "not-found";
|
|
||||||
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
|
|
||||||
} else {
|
} else {
|
||||||
oo = OperationOutcomeUtil.newInstance(getContext());
|
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulDeletes", deletedResources.size());
|
||||||
String message = getContext().getLocalizer().getMessage(BaseStorageDao.class, "successfulDeletes", deletedResources.size(), w.getMillis());
|
msg += " " + getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulTimingSuffix", w.getMillis());
|
||||||
String severity = "information";
|
oo = createInfoOperationOutcome(msg, StorageResponseCodeEnum.SUCCESSFUL_DELETE);
|
||||||
String code = "informational";
|
|
||||||
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.debug("Processed delete on {} (matched {} resource(s)) in {}ms", theUrl, deletedResources.size(), w.getMillis());
|
ourLog.debug("Processed delete on {} (matched {} resource(s)) in {}ms", theUrl, deletedResources.size(), w.getMillis());
|
||||||
|
@ -747,7 +750,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
private <MT extends IBaseMetaType> void doMetaAdd(MT theMetaAdd, BaseHasResource theEntity, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
private <MT extends IBaseMetaType> void doMetaAdd(MT theMetaAdd, BaseHasResource theEntity, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||||
IBaseResource oldVersion = toResource(theEntity, false);
|
IBaseResource oldVersion = myJpaStorageResourceParser.toResource(theEntity, false);
|
||||||
|
|
||||||
List<TagDefinition> tags = toTagList(theMetaAdd);
|
List<TagDefinition> tags = toTagList(theMetaAdd);
|
||||||
for (TagDefinition nextDef : tags) {
|
for (TagDefinition nextDef : tags) {
|
||||||
|
@ -780,7 +783,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
myEntityManager.merge(theEntity);
|
myEntityManager.merge(theEntity);
|
||||||
|
|
||||||
// Interceptor call: STORAGE_PRECOMMIT_RESOURCE_UPDATED
|
// Interceptor call: STORAGE_PRECOMMIT_RESOURCE_UPDATED
|
||||||
IBaseResource newVersion = toResource(theEntity, false);
|
IBaseResource newVersion = myJpaStorageResourceParser.toResource(theEntity, false);
|
||||||
HookParams preStorageParams = new HookParams()
|
HookParams preStorageParams = new HookParams()
|
||||||
.add(IBaseResource.class, oldVersion)
|
.add(IBaseResource.class, oldVersion)
|
||||||
.add(IBaseResource.class, newVersion)
|
.add(IBaseResource.class, newVersion)
|
||||||
|
@ -804,7 +807,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
private <MT extends IBaseMetaType> void doMetaDelete(MT theMetaDel, BaseHasResource theEntity, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
private <MT extends IBaseMetaType> void doMetaDelete(MT theMetaDel, BaseHasResource theEntity, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||||
|
|
||||||
// todo mb update hibernate search index if we are storing resources - it assumes inline tags.
|
// todo mb update hibernate search index if we are storing resources - it assumes inline tags.
|
||||||
IBaseResource oldVersion = toResource(theEntity, false);
|
IBaseResource oldVersion = myJpaStorageResourceParser.toResource(theEntity, false);
|
||||||
|
|
||||||
List<TagDefinition> tags = toTagList(theMetaDel);
|
List<TagDefinition> tags = toTagList(theMetaDel);
|
||||||
|
|
||||||
|
@ -826,7 +829,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
theEntity = myEntityManager.merge(theEntity);
|
theEntity = myEntityManager.merge(theEntity);
|
||||||
|
|
||||||
// Interceptor call: STORAGE_PRECOMMIT_RESOURCE_UPDATED
|
// Interceptor call: STORAGE_PRECOMMIT_RESOURCE_UPDATED
|
||||||
IBaseResource newVersion = toResource(theEntity, false);
|
IBaseResource newVersion = myJpaStorageResourceParser.toResource(theEntity, false);
|
||||||
HookParams preStorageParams = new HookParams()
|
HookParams preStorageParams = new HookParams()
|
||||||
.add(IBaseResource.class, oldVersion)
|
.add(IBaseResource.class, oldVersion)
|
||||||
.add(IBaseResource.class, newVersion)
|
.add(IBaseResource.class, newVersion)
|
||||||
|
@ -891,6 +894,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nonnull
|
||||||
public String getResourceName() {
|
public String getResourceName() {
|
||||||
return myResourceName;
|
return myResourceName;
|
||||||
}
|
}
|
||||||
|
@ -910,7 +914,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
@Transactional
|
@Transactional
|
||||||
public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) {
|
public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) {
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
IBundleProvider retVal = super.history(theRequestDetails, myResourceName, null, theSince, theUntil, theOffset);
|
IBundleProvider retVal = myPersistedJpaBundleProviderFactory.history(theRequestDetails, myResourceName, null, theSince, theUntil, theOffset);
|
||||||
ourLog.debug("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
ourLog.debug("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
@ -926,7 +930,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
|
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
|
||||||
BaseHasResource entity = readEntity(id, theRequest);
|
BaseHasResource entity = readEntity(id, theRequest);
|
||||||
|
|
||||||
IBundleProvider retVal = super.history(theRequest, myResourceName, entity.getId(), theSince, theUntil, theOffset);
|
IBundleProvider retVal = myPersistedJpaBundleProviderFactory.history(theRequest, myResourceName, entity.getId(), theSince, theUntil, theOffset);
|
||||||
|
|
||||||
ourLog.debug("Processed history on {} in {}ms", id, w.getMillisAndRestart());
|
ourLog.debug("Processed history on {} in {}ms", id, w.getMillisAndRestart());
|
||||||
return retVal;
|
return retVal;
|
||||||
|
@ -941,7 +945,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
|
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
|
||||||
BaseHasResource entity = readEntity(id, theRequest);
|
BaseHasResource entity = readEntity(id, theRequest);
|
||||||
|
|
||||||
IBundleProvider retVal = super.history(theRequest, myResourceName, entity.getId(),
|
IBundleProvider retVal = myPersistedJpaBundleProviderFactory.history(theRequest, myResourceName, entity.getId(),
|
||||||
theHistorySearchDateRangeParam.getLowerBoundAsInstant(),
|
theHistorySearchDateRangeParam.getLowerBoundAsInstant(),
|
||||||
theHistorySearchDateRangeParam.getUpperBoundAsInstant(),
|
theHistorySearchDateRangeParam.getUpperBoundAsInstant(),
|
||||||
theHistorySearchDateRangeParam.getOffset(),
|
theHistorySearchDateRangeParam.getOffset(),
|
||||||
|
@ -1098,68 +1102,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
return toMetaDt(theType, tagDefinitions);
|
return toMetaDt(theType, tagDefinitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DaoMethodOutcome patch(IIdType theId, String theConditionalUrl, PatchTypeEnum thePatchType, String thePatchBody, IBaseParameters theFhirPatchBody, RequestDetails theRequest) {
|
|
||||||
TransactionDetails transactionDetails = new TransactionDetails();
|
|
||||||
return myTransactionService.execute(theRequest, transactionDetails, tx -> doPatch(theId, theConditionalUrl, thePatchType, thePatchBody, theFhirPatchBody, theRequest, transactionDetails));
|
|
||||||
}
|
|
||||||
|
|
||||||
private DaoMethodOutcome doPatch(IIdType theId, String theConditionalUrl, PatchTypeEnum thePatchType, String thePatchBody, IBaseParameters theFhirPatchBody, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
|
|
||||||
ResourceTable entityToUpdate;
|
|
||||||
if (isNotBlank(theConditionalUrl)) {
|
|
||||||
|
|
||||||
Set<JpaPid> match = myMatchResourceUrlService.processMatchUrl(theConditionalUrl, myResourceType, theTransactionDetails, theRequest)
|
|
||||||
.stream().map(id -> (JpaPid) id).collect(Collectors.toSet());
|
|
||||||
if (match.size() > 1) {
|
|
||||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "transactionOperationWithMultipleMatchFailure", "PATCH", theConditionalUrl, match.size());
|
|
||||||
throw new PreconditionFailedException(Msg.code(972) + msg);
|
|
||||||
} else if (match.size() == 1) {
|
|
||||||
JpaPid pid = match.iterator().next();
|
|
||||||
entityToUpdate = myEntityManager.find(ResourceTable.class, pid.getId());
|
|
||||||
} else {
|
|
||||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "invalidMatchUrlNoMatches", theConditionalUrl);
|
|
||||||
throw new ResourceNotFoundException(Msg.code(973) + msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
entityToUpdate = readEntityLatestVersion(theId, theRequest, theTransactionDetails);
|
|
||||||
if (theId.hasVersionIdPart()) {
|
|
||||||
if (theId.getVersionIdPartAsLong() != entityToUpdate.getVersion()) {
|
|
||||||
throw new ResourceVersionConflictException(Msg.code(974) + "Version " + theId.getVersionIdPart() + " is not the most recent version of this resource, unable to apply patch");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validateResourceType(entityToUpdate);
|
|
||||||
|
|
||||||
if (isDeleted(entityToUpdate)) {
|
|
||||||
throw createResourceGoneException(entityToUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
IBaseResource resourceToUpdate = toResource(entityToUpdate, false);
|
|
||||||
IBaseResource destination;
|
|
||||||
switch (thePatchType) {
|
|
||||||
case JSON_PATCH:
|
|
||||||
destination = JsonPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody);
|
|
||||||
break;
|
|
||||||
case XML_PATCH:
|
|
||||||
destination = XmlPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody);
|
|
||||||
break;
|
|
||||||
case FHIR_PATCH_XML:
|
|
||||||
case FHIR_PATCH_JSON:
|
|
||||||
default:
|
|
||||||
IBaseParameters fhirPatchJson = theFhirPatchBody;
|
|
||||||
new FhirPatch(getContext()).apply(resourceToUpdate, fhirPatchJson);
|
|
||||||
destination = resourceToUpdate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T destinationCasted = (T) destination;
|
|
||||||
myFhirContext.newJsonParser().setParserErrorHandler(new StrictErrorHandler()).encodeResourceToString(destinationCasted);
|
|
||||||
return update(destinationCasted, null, true, theRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isDeleted(BaseHasResource entityToUpdate) {
|
private boolean isDeleted(BaseHasResource entityToUpdate) {
|
||||||
return entityToUpdate.getDeleted() != null;
|
return entityToUpdate.getDeleted() != null;
|
||||||
}
|
}
|
||||||
|
@ -1209,7 +1151,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
throw createResourceGoneException(entity.get());
|
throw createResourceGoneException(entity.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
T retVal = toResource(myResourceType, entity.get(), null, false);
|
T retVal = myJpaStorageResourceParser.toResource(myResourceType, entity.get(), null, false);
|
||||||
|
|
||||||
ourLog.debug("Processed read on {} in {}ms", jpaPid, w.getMillis());
|
ourLog.debug("Processed read on {} in {}ms", jpaPid, w.getMillis());
|
||||||
return retVal;
|
return retVal;
|
||||||
|
@ -1243,7 +1185,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
BaseHasResource entity = readEntity(theId, theRequest);
|
BaseHasResource entity = readEntity(theId, theRequest);
|
||||||
validateResourceType(entity);
|
validateResourceType(entity);
|
||||||
|
|
||||||
T retVal = toResource(myResourceType, entity, null, false);
|
T retVal = myJpaStorageResourceParser.toResource(myResourceType, entity, null, false);
|
||||||
|
|
||||||
if (theDeletedOk == false) {
|
if (theDeletedOk == false) {
|
||||||
if (isDeleted(entity)) {
|
if (isDeleted(entity)) {
|
||||||
|
@ -1297,7 +1239,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
|
|
||||||
ResourceTable entity = entityOpt.get();
|
ResourceTable entity = entityOpt.get();
|
||||||
try {
|
try {
|
||||||
T resource = (T) toResource(entity, false);
|
T resource = (T) myJpaStorageResourceParser.toResource(entity, false);
|
||||||
reindex(resource, entity);
|
reindex(resource, entity);
|
||||||
} catch (BaseServerResponseException | DataFormatException e) {
|
} catch (BaseServerResponseException | DataFormatException e) {
|
||||||
myResourceTableDao.updateIndexStatus(entity.getId(), INDEX_STATUS_INDEXING_FAILED);
|
myResourceTableDao.updateIndexStatus(entity.getId(), INDEX_STATUS_INDEXING_FAILED);
|
||||||
|
@ -1379,6 +1321,13 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IBasePersistedResource readEntityLatestVersion(ResourcePersistentId thePersistentId, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||||
|
return myEntityManager.find(ResourceTable.class, thePersistentId.getIdAsLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected ResourceTable readEntityLatestVersion(IIdType theId, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
protected ResourceTable readEntityLatestVersion(IIdType theId, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||||
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForRead(theRequestDetails, getResourceName(), theId);
|
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForRead(theRequestDetails, getResourceName(), theId);
|
||||||
|
@ -1695,8 +1644,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
private DaoMethodOutcome doUpdate(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
|
private DaoMethodOutcome doUpdate(T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequest, TransactionDetails theTransactionDetails) {
|
||||||
StopWatch w = new StopWatch();
|
|
||||||
|
|
||||||
T resource = theResource;
|
T resource = theResource;
|
||||||
|
|
||||||
preProcessResourceForStorage(resource);
|
preProcessResourceForStorage(resource);
|
||||||
|
@ -1705,6 +1652,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
ResourceTable entity = null;
|
ResourceTable entity = null;
|
||||||
|
|
||||||
IIdType resourceId;
|
IIdType resourceId;
|
||||||
|
RestOperationTypeEnum update = RestOperationTypeEnum.UPDATE;
|
||||||
if (isNotBlank(theMatchUrl)) {
|
if (isNotBlank(theMatchUrl)) {
|
||||||
Set<JpaPid> match = myMatchResourceUrlService.processMatchUrl(theMatchUrl, myResourceType, theTransactionDetails, theRequest, theResource)
|
Set<JpaPid> match = myMatchResourceUrlService.processMatchUrl(theMatchUrl, myResourceType, theTransactionDetails, theRequest, theResource)
|
||||||
.stream().map(id -> (JpaPid) id).collect(Collectors.toSet());
|
.stream().map(id -> (JpaPid) id).collect(Collectors.toSet());
|
||||||
|
@ -1716,7 +1664,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
entity = myEntityManager.find(ResourceTable.class, pid.getId());
|
entity = myEntityManager.find(ResourceTable.class, pid.getId());
|
||||||
resourceId = entity.getIdDt();
|
resourceId = entity.getIdDt();
|
||||||
} else {
|
} else {
|
||||||
DaoMethodOutcome outcome = create(resource, null, thePerformIndexing, theTransactionDetails, theRequest);
|
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineCreatePartitionForRequest(theRequest, theResource, getResourceName());
|
||||||
|
DaoMethodOutcome outcome = doCreateForPostOrPut(theRequest, resource, theMatchUrl, false, thePerformIndexing, requestPartitionId, update, theTransactionDetails);
|
||||||
|
|
||||||
// Pre-cache the match URL
|
// Pre-cache the match URL
|
||||||
if (outcome.getPersistentId() != null) {
|
if (outcome.getPersistentId() != null) {
|
||||||
|
@ -1755,86 +1704,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
}
|
}
|
||||||
|
|
||||||
if (create) {
|
if (create) {
|
||||||
return doCreateForPostOrPut(resource, null, thePerformIndexing, theTransactionDetails, theRequest, requestPartitionId);
|
return doCreateForPostOrPut(theRequest, resource, null, false, thePerformIndexing, requestPartitionId, update, theTransactionDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resourceId.hasVersionIdPart() && Long.parseLong(resourceId.getVersionIdPart()) != entity.getVersion()) {
|
// Start
|
||||||
throw new ResourceVersionConflictException(Msg.code(989) + "Trying to update " + resourceId + " but this is not the current version");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) {
|
return doUpdateForUpdateOrPatch(theRequest, resourceId, theMatchUrl, thePerformIndexing, theForceUpdateVersion, resource, entity, update, theTransactionDetails);
|
||||||
throw new UnprocessableEntityException(Msg.code(990) + "Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
IBaseResource oldResource;
|
|
||||||
if (getConfig().isMassIngestionMode()) {
|
|
||||||
oldResource = null;
|
|
||||||
} else {
|
|
||||||
oldResource = toResource(entity, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Mark the entity as not deleted - This is also done in the actual updateInternal()
|
|
||||||
* method later on so it usually doesn't matter whether we do it here, but in the
|
|
||||||
* case of a transaction with multiple PUTs we don't get there until later so
|
|
||||||
* having this here means that a transaction can have a reference in one
|
|
||||||
* resource to another resource in the same transaction that is being
|
|
||||||
* un-deleted by the transaction. Wacky use case, sure. But it's real.
|
|
||||||
*
|
|
||||||
* See SystemProviderR4Test#testTransactionReSavesPreviouslyDeletedResources
|
|
||||||
* for a test that needs this.
|
|
||||||
*/
|
|
||||||
boolean wasDeleted = isDeleted(entity);
|
|
||||||
entity.setDeleted(null);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we aren't indexing, that means we're doing this inside a transaction.
|
|
||||||
* The transaction will do the actual storage to the database a bit later on,
|
|
||||||
* after placeholder IDs have been replaced, by calling {@link #updateInternal}
|
|
||||||
* directly. So we just bail now.
|
|
||||||
*/
|
|
||||||
if (!thePerformIndexing) {
|
|
||||||
resource.setId(entity.getIdDt().getValue());
|
|
||||||
DaoMethodOutcome outcome = toMethodOutcome(theRequest, entity, resource).setCreated(wasDeleted);
|
|
||||||
outcome.setPreviousResource(oldResource);
|
|
||||||
if (!outcome.isNop()) {
|
|
||||||
// Technically this may not end up being right since we might not increment if the
|
|
||||||
// contents turn out to be the same
|
|
||||||
outcome.setId(outcome.getId().withVersion(Long.toString(outcome.getId().getVersionIdPartAsLong() + 1)));
|
|
||||||
}
|
|
||||||
return outcome;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Otherwise, we're not in a transaction
|
|
||||||
*/
|
|
||||||
ResourceTable savedEntity = updateInternal(theRequest, resource, thePerformIndexing, theForceUpdateVersion, entity, resourceId, oldResource, theTransactionDetails);
|
|
||||||
|
|
||||||
if (thePerformIndexing) {
|
|
||||||
Collection<? extends BaseTag> tagList = Collections.emptyList();
|
|
||||||
if (entity.isHasTags()) {
|
|
||||||
tagList = entity.getTags();
|
|
||||||
}
|
|
||||||
long version = entity.getVersion();
|
|
||||||
populateResourceMetadata(entity, false, tagList, version, getResourceType(), resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
DaoMethodOutcome outcome = toMethodOutcome(theRequest, savedEntity, resource).setCreated(wasDeleted);
|
|
||||||
|
|
||||||
if (!thePerformIndexing) {
|
|
||||||
IIdType id = getContext().getVersion().newIdType();
|
|
||||||
id.setValue(entity.getIdDt().getValue());
|
|
||||||
outcome.setId(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulUpdate", outcome.getId(), w.getMillisAndRestart());
|
|
||||||
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
|
|
||||||
|
|
||||||
ourLog.debug(msg);
|
|
||||||
return outcome;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method for updating the historical version of the resource when a history version id is included in the request.
|
* Method for updating the historical version of the resource when a history version id is included in the request.
|
||||||
*
|
*
|
||||||
|
@ -1849,8 +1729,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
// No need for indexing as this will update a non-current version of the resource which will not be searchable
|
// No need for indexing as this will update a non-current version of the resource which will not be searchable
|
||||||
preProcessResourceForStorage(theResource, theRequest, theTransactionDetails, false);
|
preProcessResourceForStorage(theResource, theRequest, theTransactionDetails, false);
|
||||||
|
|
||||||
BaseHasResource entity = null;
|
BaseHasResource entity;
|
||||||
BaseHasResource currentEntity = null;
|
BaseHasResource currentEntity;
|
||||||
|
|
||||||
IIdType resourceId;
|
IIdType resourceId;
|
||||||
|
|
||||||
|
@ -1879,12 +1759,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
||||||
entity.setDeleted(null);
|
entity.setDeleted(null);
|
||||||
boolean isUpdatingCurrent = resourceId.hasVersionIdPart() && Long.parseLong(resourceId.getVersionIdPart()) == currentEntity.getVersion();
|
boolean isUpdatingCurrent = resourceId.hasVersionIdPart() && Long.parseLong(resourceId.getVersionIdPart()) == currentEntity.getVersion();
|
||||||
IBasePersistedResource savedEntity = updateHistoryEntity(theRequest, theResource, currentEntity, entity, resourceId, theTransactionDetails, isUpdatingCurrent);
|
IBasePersistedResource savedEntity = updateHistoryEntity(theRequest, theResource, currentEntity, entity, resourceId, theTransactionDetails, isUpdatingCurrent);
|
||||||
DaoMethodOutcome outcome = toMethodOutcome(theRequest, savedEntity, theResource).setCreated(wasDeleted);
|
DaoMethodOutcome outcome = toMethodOutcome(theRequest, savedEntity, theResource, null, RestOperationTypeEnum.UPDATE).setCreated(wasDeleted);
|
||||||
|
|
||||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulUpdate", outcome.getId(), w.getMillisAndRestart());
|
populateOperationOutcomeForUpdate(w, outcome, null, RestOperationTypeEnum.UPDATE);
|
||||||
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
|
|
||||||
|
|
||||||
ourLog.debug(msg);
|
|
||||||
return outcome;
|
return outcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
package ca.uhn.fhir.jpa.dao;
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.expunge.ExpungeService;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory;
|
||||||
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.util.QueryChunker;
|
import ca.uhn.fhir.jpa.util.QueryChunker;
|
||||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||||
|
@ -26,6 +34,9 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.PersistenceContext;
|
||||||
|
import javax.persistence.PersistenceContextType;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
@ -59,25 +70,36 @@ import java.util.stream.Collectors;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends BaseHapiFhirDao<IBaseResource> implements IFhirSystemDao<T, MT> {
|
public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends BaseStorageDao implements IFhirSystemDao<T, MT> {
|
||||||
|
|
||||||
public static final Predicate[] EMPTY_PREDICATE_ARRAY = new Predicate[0];
|
public static final Predicate[] EMPTY_PREDICATE_ARRAY = new Predicate[0];
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
|
||||||
public ResourceCountCache myResourceCountsCache;
|
public ResourceCountCache myResourceCountsCache;
|
||||||
|
|
||||||
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
|
protected EntityManager myEntityManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
private TransactionProcessor myTransactionProcessor;
|
private TransactionProcessor myTransactionProcessor;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationContext myApplicationContext;
|
private ApplicationContext myApplicationContext;
|
||||||
|
@Autowired
|
||||||
|
private ExpungeService myExpungeService;
|
||||||
|
@Autowired
|
||||||
|
private IResourceTableDao myResourceTableDao;
|
||||||
|
@Autowired
|
||||||
|
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||||
|
@Autowired
|
||||||
|
private IResourceTagDao myResourceTagDao;
|
||||||
|
@Autowired
|
||||||
|
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void setTransactionProcessorForUnitTest(TransactionProcessor theTransactionProcessor) {
|
public void setTransactionProcessorForUnitTest(TransactionProcessor theTransactionProcessor) {
|
||||||
myTransactionProcessor = theTransactionProcessor;
|
myTransactionProcessor = theTransactionProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void start() {
|
public void start() {
|
||||||
super.start();
|
|
||||||
myTransactionProcessor.setDao(this);
|
myTransactionProcessor.setDao(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +147,7 @@ public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends B
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) {
|
public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) {
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
IBundleProvider retVal = super.history(theRequestDetails, null, null, theSince, theUntil, theOffset);
|
IBundleProvider retVal = myPersistedJpaBundleProviderFactory.history(theRequestDetails, null, null, theSince, theUntil, theOffset);
|
||||||
ourLog.info("Processed global history in {}ms", w.getMillisAndRestart());
|
ourLog.info("Processed global history in {}ms", w.getMillisAndRestart());
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
@ -260,4 +282,25 @@ public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends B
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IInterceptorBroadcaster getInterceptorBroadcaster() {
|
||||||
|
return myInterceptorBroadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DaoConfig getConfig() {
|
||||||
|
return myDaoConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FhirContext getContext() {
|
||||||
|
return myFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setDaoConfigForUnitTest(DaoConfig theDaoConfig) {
|
||||||
|
myDaoConfig = theDaoConfig;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Composition;
|
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
public class FhirResourceDaoCompositionDstu2 extends BaseHapiFhirResourceDao<Composition>implements IFhirResourceDaoComposition<Composition> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
|
|
||||||
throw new NotImplementedOperationException(Msg.code(955) + "$document not implemented in DSTU2");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
|
||||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_40;
|
|
||||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_40;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
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> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
|
||||||
private FhirContext myDstu2Hl7OrgContext = FhirContext.forDstu2Hl7Org();
|
|
||||||
|
|
||||||
protected void reindexAffectedResources(SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
|
||||||
String expression = theResource != null ? theResource.getXpath() : null;
|
|
||||||
List<String> base = theResource != null ? Lists.newArrayList(theResource.getBase()) : null;
|
|
||||||
requestReindexForRelatedResources(reindex, base, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postPersist(theEntity, theResource, theRequestDetails);
|
|
||||||
reindexAffectedResources(theResource, theRequestDetails);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postUpdate(theEntity, theResource, theRequestDetails);
|
|
||||||
reindexAffectedResources(theResource, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
|
||||||
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
|
|
||||||
reindexAffectedResources(theResourceToDelete, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
|
||||||
|
|
||||||
String encoded = getContext().newJsonParser().encodeResourceToString(theResource);
|
|
||||||
org.hl7.fhir.dstu2.model.SearchParameter hl7Org = myDstu2Hl7OrgContext.newJsonParser().parseResource(org.hl7.fhir.dstu2.model.SearchParameter.class, encoded);
|
|
||||||
|
|
||||||
org.hl7.fhir.r4.model.SearchParameter convertedSp = (org.hl7.fhir.r4.model.SearchParameter) VersionConvertorFactory_10_40.convertResource(hl7Org, new BaseAdvisor_10_40(false));
|
|
||||||
if (isBlank(convertedSp.getExpression()) && isNotBlank(hl7Org.getXpath())) {
|
|
||||||
convertedSp.setExpression(hl7Org.getXpath());
|
|
||||||
}
|
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(convertedSp, getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||||
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2022 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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.BaseTag;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public interface IJpaStorageResourceParser extends IStorageResourceParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a storage entity into a FHIR resource model instance
|
||||||
|
*/
|
||||||
|
<R extends IBaseResource> R toResource(Class<R> theResourceType, IBaseResourceEntity theEntity, Collection<ResourceTag> theTagList, boolean theForHistoryOperation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the metadata (Resource.meta.*) from a storage entity and other related
|
||||||
|
* objects pulled from the database
|
||||||
|
*/
|
||||||
|
<R extends IBaseResource> R populateResourceMetadata(IBaseResourceEntity theEntitySource, boolean theForHistoryOperation, @Nullable Collection<? extends BaseTag> tagList, long theVersion, R theResourceTarget);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates a resource model object's metadata (Resource.meta.*) based on the
|
||||||
|
* values from a stroage entity.
|
||||||
|
*
|
||||||
|
* @param theEntitySource The source
|
||||||
|
* @param theResourceTarget The target
|
||||||
|
*/
|
||||||
|
void updateResourceMetadata(IBaseResourceEntity theEntitySource, IBaseResource theResourceTarget);
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
|
@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
|
@ -29,14 +28,14 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.Composition;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
public class FhirResourceDaoCompositionR4 extends BaseHapiFhirResourceDao<Composition> implements IFhirResourceDaoComposition<Composition> {
|
public class JpaResourceDaoComposition<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> implements IFhirResourceDaoComposition<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
|
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
|
|
@ -29,29 +29,70 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
|
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.PersistenceContext;
|
||||||
|
import javax.persistence.PersistenceContextType;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
public abstract class BaseHapiFhirResourceDaoObservation<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> implements IFhirResourceDaoObservation<T> {
|
public class JpaResourceDaoObservation<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> implements IFhirResourceDaoObservation<T> {
|
||||||
|
|
||||||
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
|
protected EntityManager myEntityManager;
|
||||||
@Autowired
|
@Autowired
|
||||||
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
|
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IRequestPartitionHelperSvc myRequestPartitionHelperService;
|
private IRequestPartitionHelperSvc myRequestPartitionHelperService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
||||||
|
updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails);
|
||||||
|
|
||||||
|
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null);
|
||||||
|
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getEffectiveParamName() {
|
||||||
|
return Observation.SP_DATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCodeParamName() {
|
||||||
|
return Observation.SP_CODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSubjectParamName() {
|
||||||
|
return Observation.SP_SUBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPatientParamName() {
|
||||||
|
return Observation.SP_PATIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
||||||
|
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
||||||
|
return updateObservationEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull,
|
||||||
|
thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate,
|
||||||
|
theCreateNewHistoryEntry);
|
||||||
|
}
|
||||||
|
|
||||||
protected ResourceTable updateObservationEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity,
|
protected ResourceTable updateObservationEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity,
|
||||||
Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
|
Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
|
||||||
TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
||||||
|
@ -130,12 +171,4 @@ public abstract class BaseHapiFhirResourceDaoObservation<T extends IBaseResource
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected String getEffectiveParamName();
|
|
||||||
|
|
||||||
abstract protected String getCodeParamName();
|
|
||||||
|
|
||||||
abstract protected String getSubjectParamName();
|
|
||||||
|
|
||||||
abstract protected String getPatientParamName();
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
@ -6,18 +6,18 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
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.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
import ca.uhn.fhir.util.ElementUtil;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
|
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r5.model.CodeType;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r5.model.Enumerations;
|
||||||
import org.hl7.fhir.r4.model.SearchParameter;
|
import org.hl7.fhir.r5.model.SearchParameter;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -46,60 +46,63 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
public class JpaResourceDaoSearchParameter<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> implements IFhirResourceDaoSearchParameter<T> {
|
||||||
|
|
||||||
private static final Pattern REGEX_SP_EXPRESSION_HAS_PATH = Pattern.compile("[( ]*([A-Z][a-zA-Z]+\\.)?[a-z].*");
|
private static final Pattern REGEX_SP_EXPRESSION_HAS_PATH = Pattern.compile("[( ]*([A-Z][a-zA-Z]+\\.)?[a-z].*");
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
private VersionCanonicalizer myVersionCanonicalizer;
|
||||||
|
|
||||||
protected void reindexAffectedResources(SearchParameter theResource, RequestDetails theRequestDetails) {
|
protected void reindexAffectedResources(T theResource, RequestDetails theRequestDetails) {
|
||||||
|
// N.B. Don't do this on the canonicalized version
|
||||||
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
||||||
String expression = theResource != null ? theResource.getExpression() : null;
|
|
||||||
List<String> base = theResource != null ? theResource.getBase().stream().map(CodeType::getCode).collect(Collectors.toList()) : null;
|
org.hl7.fhir.r5.model.SearchParameter searchParameter = myVersionCanonicalizer.searchParameterToCanonical(theResource);
|
||||||
|
List<String> base = theResource != null ? searchParameter.getBase().stream().map(CodeType::getCode).collect(Collectors.toList()) : null;
|
||||||
requestReindexForRelatedResources(reindex, base, theRequestDetails);
|
requestReindexForRelatedResources(reindex, base, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
protected void postPersist(ResourceTable theEntity, T theResource, RequestDetails theRequestDetails) {
|
||||||
super.postPersist(theEntity, theResource, theRequestDetails);
|
super.postPersist(theEntity, theResource, theRequestDetails);
|
||||||
reindexAffectedResources(theResource, theRequestDetails);
|
reindexAffectedResources(theResource, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
protected void postUpdate(ResourceTable theEntity, T theResource, RequestDetails theRequestDetails) {
|
||||||
super.postUpdate(theEntity, theResource, theRequestDetails);
|
super.postUpdate(theEntity, theResource, theRequestDetails);
|
||||||
reindexAffectedResources(theResource, theRequestDetails);
|
reindexAffectedResources(theResource, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
protected void preDelete(T theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
||||||
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
|
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
|
||||||
reindexAffectedResources(theResourceToDelete, theRequestDetails);
|
reindexAffectedResources(theResourceToDelete, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
super.validateResourceForStorage(theResource, theEntityToSave);
|
||||||
|
|
||||||
validateSearchParam(theResource, getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
validateSearchParam(theResource, getContext(), getConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void validateSearchParam(SearchParameter theResource, FhirContext theContext, DaoConfig theDaoConfig, ISearchParamRegistry theSearchParamRegistry, ISearchParamExtractor theSearchParamExtractor) {
|
public void validateSearchParam(IBaseResource theResource, FhirContext theContext, DaoConfig theDaoConfig) {
|
||||||
|
org.hl7.fhir.r5.model.SearchParameter searchParameter = myVersionCanonicalizer.searchParameterToCanonical(theResource);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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 : theResource.getBase()) {
|
for (IPrimitiveType<?> nextBaseType : searchParameter.getBase()) {
|
||||||
String nextBase = nextBaseType.getValueAsString();
|
String nextBase = nextBaseType.getValueAsString();
|
||||||
RuntimeSearchParam existingSearchParam = theSearchParamRegistry.getActiveSearchParam(nextBase, theResource.getCode());
|
RuntimeSearchParam existingSearchParam = mySearchParamRegistry.getActiveSearchParam(nextBase, searchParameter.getCode());
|
||||||
if (existingSearchParam != null) {
|
if (existingSearchParam != null) {
|
||||||
boolean isBuiltIn = existingSearchParam.getId() == null;
|
boolean isBuiltIn = existingSearchParam.getId() == null;
|
||||||
isBuiltIn |= existingSearchParam.getUri().startsWith("http://hl7.org/fhir/SearchParameter/");
|
isBuiltIn |= existingSearchParam.getUri().startsWith("http://hl7.org/fhir/SearchParameter/");
|
||||||
if (isBuiltIn) {
|
if (isBuiltIn) {
|
||||||
throw new UnprocessableEntityException(Msg.code(1111) + "Can not override built-in search parameter " + nextBase + ":" + theResource.getCode() + " because overriding is disabled on this server");
|
throw new UnprocessableEntityException(Msg.code(1111) + "Can not override built-in search parameter " + nextBase + ":" + searchParameter.getCode() + " because overriding is disabled on this server");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,34 +112,34 @@ 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 (theResource.getStatus() == null) {
|
if (searchParameter.getStatus() == null) {
|
||||||
throw new UnprocessableEntityException(Msg.code(1112) + "SearchParameter.status is missing or invalid");
|
throw new UnprocessableEntityException(Msg.code(1112) + "SearchParameter.status is missing or invalid");
|
||||||
}
|
}
|
||||||
if (!theResource.getStatus().name().equals("ACTIVE")) {
|
if (!searchParameter.getStatus().name().equals("ACTIVE")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ElementUtil.isEmpty(theResource.getBase()) && (theResource.getType() == null || !Enumerations.SearchParamType.COMPOSITE.name().equals(theResource.getType().name()))) {
|
if (ElementUtil.isEmpty(searchParameter.getBase()) && (searchParameter.getType() == null || !Enumerations.SearchParamType.COMPOSITE.name().equals(searchParameter.getType().name()))) {
|
||||||
throw new UnprocessableEntityException(Msg.code(1113) + "SearchParameter.base is missing");
|
throw new UnprocessableEntityException(Msg.code(1113) + "SearchParameter.base is missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isUnique = hasAnyExtensionUniqueSetTo(theResource, true);
|
boolean isUnique = hasAnyExtensionUniqueSetTo(searchParameter, true);
|
||||||
|
|
||||||
if (theResource.getType() != null && theResource.getType().name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(theResource.getExpression())) {
|
if (searchParameter.getType() != null && searchParameter.getType().name().equals(Enumerations.SearchParamType.COMPOSITE.name()) && isBlank(searchParameter.getExpression())) {
|
||||||
|
|
||||||
// this is ok
|
// this is ok
|
||||||
|
|
||||||
} else if (isBlank(theResource.getExpression())) {
|
} else if (isBlank(searchParameter.getExpression())) {
|
||||||
|
|
||||||
throw new UnprocessableEntityException(Msg.code(1114) + "SearchParameter.expression is missing");
|
throw new UnprocessableEntityException(Msg.code(1114) + "SearchParameter.expression is missing");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (isUnique) {
|
if (isUnique) {
|
||||||
if (theResource.getComponent().size() == 0) {
|
if (searchParameter.getComponent().size() == 0) {
|
||||||
throw new UnprocessableEntityException(Msg.code(1115) + "SearchParameter is marked as unique but has no components");
|
throw new UnprocessableEntityException(Msg.code(1115) + "SearchParameter is marked as unique but has no components");
|
||||||
}
|
}
|
||||||
for (SearchParameter.SearchParameterComponentComponent next : theResource.getComponent()) {
|
for (SearchParameter.SearchParameterComponentComponent next : searchParameter.getComponent()) {
|
||||||
if (isBlank(next.getDefinition())) {
|
if (isBlank(next.getDefinition())) {
|
||||||
throw new UnprocessableEntityException(Msg.code(1116) + "SearchParameter is marked as unique but is missing component.definition");
|
throw new UnprocessableEntityException(Msg.code(1116) + "SearchParameter is marked as unique but is missing component.definition");
|
||||||
}
|
}
|
||||||
|
@ -146,14 +149,13 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
FhirVersionEnum fhirVersion = theContext.getVersion().getVersion();
|
FhirVersionEnum fhirVersion = theContext.getVersion().getVersion();
|
||||||
if (fhirVersion.isOlderThan(FhirVersionEnum.DSTU3)) {
|
if (fhirVersion.isOlderThan(FhirVersionEnum.DSTU3)) {
|
||||||
// omitting validation for DSTU2_HL7ORG, DSTU2_1 and DSTU2
|
// omitting validation for DSTU2_HL7ORG, DSTU2_1 and DSTU2
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
|
|
||||||
if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
|
if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
|
||||||
|
|
||||||
validateExpressionPath(theResource);
|
validateExpressionPath(searchParameter);
|
||||||
|
|
||||||
String expression = getExpression(theResource);
|
String expression = getExpression(searchParameter);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
theContext.newFhirPath().parse(expression);
|
theContext.newFhirPath().parse(expression);
|
||||||
|
@ -166,7 +168,12 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateExpressionPath(SearchParameter theSearchParameter){
|
@VisibleForTesting
|
||||||
|
void setVersionCanonicalizerForUnitTest(VersionCanonicalizer theVersionCanonicalizer) {
|
||||||
|
myVersionCanonicalizer = theVersionCanonicalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateExpressionPath(SearchParameter theSearchParameter) {
|
||||||
String expression = getExpression(theSearchParameter);
|
String expression = getExpression(theSearchParameter);
|
||||||
|
|
||||||
boolean isResourceOfTypeComposite = theSearchParameter.getType() == Enumerations.SearchParamType.COMPOSITE;
|
boolean isResourceOfTypeComposite = theSearchParameter.getType() == Enumerations.SearchParamType.COMPOSITE;
|
||||||
|
@ -175,23 +182,23 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
||||||
|
|
||||||
boolean isUnique = hasAnyExtensionUniqueSetTo(theSearchParameter, true);
|
boolean isUnique = hasAnyExtensionUniqueSetTo(theSearchParameter, true);
|
||||||
|
|
||||||
if ( !isUnique && !isResourceOfTypeComposite && !isResourceOfTypeSpecial && !expressionHasPath ) {
|
if (!isUnique && !isResourceOfTypeComposite && !isResourceOfTypeSpecial && !expressionHasPath) {
|
||||||
throw new UnprocessableEntityException(Msg.code(1120) + "SearchParameter.expression value \"" + expression + "\" is invalid due to missing/incorrect path");
|
throw new UnprocessableEntityException(Msg.code(1120) + "SearchParameter.expression value \"" + expression + "\" is invalid due to missing/incorrect path");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getExpression(SearchParameter theSearchParameter){
|
private static String getExpression(SearchParameter theSearchParameter) {
|
||||||
return theSearchParameter.getExpression().trim();
|
return theSearchParameter.getExpression().trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasAnyExtensionUniqueSetTo(SearchParameter theSearchParameter, boolean theValue){
|
private static boolean hasAnyExtensionUniqueSetTo(SearchParameter theSearchParameter, boolean theValue) {
|
||||||
String theValueAsString = Boolean.toString(theValue);
|
String theValueAsString = Boolean.toString(theValue);
|
||||||
|
|
||||||
return theSearchParameter
|
return theSearchParameter
|
||||||
.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE)
|
.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE)
|
||||||
.stream()
|
.stream()
|
||||||
.anyMatch(t-> theValueAsString.equals(t.getValueAsPrimitive().getValueAsString()));
|
.anyMatch(t -> theValueAsString.equals(t.getValueAsPrimitive().getValueAsString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,490 @@
|
||||||
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2022 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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||||
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
|
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
|
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.BaseTag;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||||
|
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.parser.LenientErrorHandler;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.util.MetaUtil;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.cleanProvenanceSourceUri;
|
||||||
|
import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.decodeResource;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
public class JpaStorageResourceParser implements IJpaStorageResourceParser {
|
||||||
|
public static final LenientErrorHandler LENIENT_ERROR_HANDLER = new LenientErrorHandler(false).setErrorOnInvalidValue(false);
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(JpaStorageResourceParser.class);
|
||||||
|
@Autowired
|
||||||
|
private FhirContext myContext;
|
||||||
|
@Autowired
|
||||||
|
private DaoConfig myDaoConfig;
|
||||||
|
@Autowired
|
||||||
|
private IResourceHistoryTableDao myResourceHistoryTableDao;
|
||||||
|
@Autowired
|
||||||
|
private PartitionSettings myPartitionSettings;
|
||||||
|
@Autowired
|
||||||
|
private IPartitionLookupSvc myPartitionLookupSvc;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBaseResource toResource(IBasePersistedResource theEntity, boolean theForHistoryOperation) {
|
||||||
|
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
|
||||||
|
Class<? extends IBaseResource> resourceType = type.getImplementingClass();
|
||||||
|
return toResource(resourceType, (IBaseResourceEntity) theEntity, null, theForHistoryOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R extends IBaseResource> R toResource(Class<R> theResourceType, IBaseResourceEntity theEntity, Collection<ResourceTag> theTagList, boolean theForHistoryOperation) {
|
||||||
|
|
||||||
|
// 1. get resource, it's encoding and the tags if any
|
||||||
|
byte[] resourceBytes;
|
||||||
|
String resourceText;
|
||||||
|
ResourceEncodingEnum resourceEncoding;
|
||||||
|
@Nullable
|
||||||
|
Collection<? extends BaseTag> tagList = Collections.emptyList();
|
||||||
|
long version;
|
||||||
|
String provenanceSourceUri = null;
|
||||||
|
String provenanceRequestId = null;
|
||||||
|
|
||||||
|
if (theEntity instanceof ResourceHistoryTable) {
|
||||||
|
ResourceHistoryTable history = (ResourceHistoryTable) theEntity;
|
||||||
|
resourceBytes = history.getResource();
|
||||||
|
resourceText = history.getResourceTextVc();
|
||||||
|
resourceEncoding = history.getEncoding();
|
||||||
|
switch (myDaoConfig.getTagStorageMode()) {
|
||||||
|
case VERSIONED:
|
||||||
|
default:
|
||||||
|
if (history.isHasTags()) {
|
||||||
|
tagList = history.getTags();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NON_VERSIONED:
|
||||||
|
if (history.getResourceTable().isHasTags()) {
|
||||||
|
tagList = history.getResourceTable().getTags();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INLINE:
|
||||||
|
tagList = null;
|
||||||
|
}
|
||||||
|
version = history.getVersion();
|
||||||
|
if (history.getProvenance() != null) {
|
||||||
|
provenanceRequestId = history.getProvenance().getRequestId();
|
||||||
|
provenanceSourceUri = history.getProvenance().getSourceUri();
|
||||||
|
}
|
||||||
|
} else if (theEntity instanceof ResourceTable) {
|
||||||
|
ResourceTable resource = (ResourceTable) theEntity;
|
||||||
|
ResourceHistoryTable history;
|
||||||
|
if (resource.getCurrentVersionEntity() != null) {
|
||||||
|
history = resource.getCurrentVersionEntity();
|
||||||
|
} else {
|
||||||
|
version = theEntity.getVersion();
|
||||||
|
history = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(theEntity.getId(), version);
|
||||||
|
((ResourceTable) theEntity).setCurrentVersionEntity(history);
|
||||||
|
|
||||||
|
while (history == null) {
|
||||||
|
if (version > 1L) {
|
||||||
|
version--;
|
||||||
|
history = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(theEntity.getId(), version);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceBytes = history.getResource();
|
||||||
|
resourceEncoding = history.getEncoding();
|
||||||
|
resourceText = history.getResourceTextVc();
|
||||||
|
switch (myDaoConfig.getTagStorageMode()) {
|
||||||
|
case VERSIONED:
|
||||||
|
case NON_VERSIONED:
|
||||||
|
if (resource.isHasTags()) {
|
||||||
|
tagList = resource.getTags();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INLINE:
|
||||||
|
tagList = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
version = history.getVersion();
|
||||||
|
if (history.getProvenance() != null) {
|
||||||
|
provenanceRequestId = history.getProvenance().getRequestId();
|
||||||
|
provenanceSourceUri = history.getProvenance().getSourceUri();
|
||||||
|
}
|
||||||
|
} else if (theEntity instanceof ResourceSearchView) {
|
||||||
|
// This is the search View
|
||||||
|
ResourceSearchView view = (ResourceSearchView) theEntity;
|
||||||
|
resourceBytes = view.getResource();
|
||||||
|
resourceText = view.getResourceTextVc();
|
||||||
|
resourceEncoding = view.getEncoding();
|
||||||
|
version = view.getVersion();
|
||||||
|
provenanceRequestId = view.getProvenanceRequestId();
|
||||||
|
provenanceSourceUri = view.getProvenanceSourceUri();
|
||||||
|
switch (myDaoConfig.getTagStorageMode()) {
|
||||||
|
case VERSIONED:
|
||||||
|
case NON_VERSIONED:
|
||||||
|
if (theTagList != null) {
|
||||||
|
tagList = theTagList;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INLINE:
|
||||||
|
tagList = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// something wrong
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. get The text
|
||||||
|
String decodedResourceText = decodedResourceText(resourceBytes, resourceText, resourceEncoding);
|
||||||
|
|
||||||
|
// 3. Use the appropriate custom type if one is specified in the context
|
||||||
|
Class<R> resourceType = determineTypeToParse(theResourceType, tagList);
|
||||||
|
|
||||||
|
// 4. parse the text to FHIR
|
||||||
|
R retVal = parseResource(theEntity, resourceEncoding, decodedResourceText, resourceType);
|
||||||
|
|
||||||
|
// 5. fill MetaData
|
||||||
|
retVal = populateResourceMetadata(theEntity, theForHistoryOperation, tagList, version, retVal);
|
||||||
|
|
||||||
|
// 6. Handle source (provenance)
|
||||||
|
populateResourceSource(provenanceSourceUri, provenanceRequestId, retVal);
|
||||||
|
|
||||||
|
// 7. Add partition information
|
||||||
|
populateResourcePartitionInformation(theEntity, retVal);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <R extends IBaseResource> void populateResourcePartitionInformation(IBaseResourceEntity theEntity, R retVal) {
|
||||||
|
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||||
|
PartitionablePartitionId partitionId = theEntity.getPartitionId();
|
||||||
|
if (partitionId != null && partitionId.getPartitionId() != null) {
|
||||||
|
PartitionEntity persistedPartition = myPartitionLookupSvc.getPartitionById(partitionId.getPartitionId());
|
||||||
|
retVal.setUserData(Constants.RESOURCE_PARTITION_ID, persistedPartition.toRequestPartitionId());
|
||||||
|
} else {
|
||||||
|
retVal.setUserData(Constants.RESOURCE_PARTITION_ID, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <R extends IBaseResource> void populateResourceSource(String provenanceSourceUri, String provenanceRequestId, R retVal) {
|
||||||
|
if (isNotBlank(provenanceRequestId) || isNotBlank(provenanceSourceUri)) {
|
||||||
|
String sourceString = cleanProvenanceSourceUri(provenanceSourceUri)
|
||||||
|
+ (isNotBlank(provenanceRequestId) ? "#" : "")
|
||||||
|
+ defaultString(provenanceRequestId);
|
||||||
|
|
||||||
|
MetaUtil.setSource(myContext, retVal, sourceString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <R extends IBaseResource> R parseResource(IBaseResourceEntity theEntity, ResourceEncodingEnum resourceEncoding, String decodedResourceText, Class<R> resourceType) {
|
||||||
|
R retVal;
|
||||||
|
if (resourceEncoding != ResourceEncodingEnum.DEL) {
|
||||||
|
|
||||||
|
IParser parser = new TolerantJsonParser(getContext(theEntity.getFhirVersion()), LENIENT_ERROR_HANDLER, theEntity.getId());
|
||||||
|
|
||||||
|
try {
|
||||||
|
retVal = parser.parseResource(resourceType, decodedResourceText);
|
||||||
|
} catch (Exception e) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append("Failed to parse database resource[");
|
||||||
|
b.append(myContext.getResourceType(resourceType));
|
||||||
|
b.append("/");
|
||||||
|
b.append(theEntity.getIdDt().getIdPart());
|
||||||
|
b.append(" (pid ");
|
||||||
|
b.append(theEntity.getId());
|
||||||
|
b.append(", version ");
|
||||||
|
b.append(theEntity.getFhirVersion().name());
|
||||||
|
b.append("): ");
|
||||||
|
b.append(e.getMessage());
|
||||||
|
String msg = b.toString();
|
||||||
|
ourLog.error(msg, e);
|
||||||
|
throw new DataFormatException(Msg.code(928) + msg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
retVal = (R) myContext.getResourceDefinition(theEntity.getResourceType()).newInstance();
|
||||||
|
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <R extends IBaseResource> Class<R> determineTypeToParse(Class<R> theResourceType, @Nullable Collection<? extends BaseTag> tagList) {
|
||||||
|
Class<R> resourceType = theResourceType;
|
||||||
|
if (tagList != null) {
|
||||||
|
if (myContext.hasDefaultTypeForProfile()) {
|
||||||
|
for (BaseTag nextTag : tagList) {
|
||||||
|
if (nextTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
|
||||||
|
String profile = nextTag.getTag().getCode();
|
||||||
|
if (isNotBlank(profile)) {
|
||||||
|
Class<? extends IBaseResource> newType = myContext.getDefaultTypeForProfile(profile);
|
||||||
|
if (newType != null && theResourceType.isAssignableFrom(newType)) {
|
||||||
|
ourLog.debug("Using custom type {} for profile: {}", newType.getName(), profile);
|
||||||
|
resourceType = (Class<R>) newType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resourceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <R extends IBaseResource> R populateResourceMetadata(IBaseResourceEntity theEntitySource, boolean theForHistoryOperation, @Nullable Collection<? extends BaseTag> tagList, long theVersion, R theResourceTarget) {
|
||||||
|
if (theResourceTarget instanceof IResource) {
|
||||||
|
IResource res = (IResource) theResourceTarget;
|
||||||
|
theResourceTarget = (R) populateResourceMetadataHapi(theEntitySource, tagList, theForHistoryOperation, res, theVersion);
|
||||||
|
} else {
|
||||||
|
IAnyResource res = (IAnyResource) theResourceTarget;
|
||||||
|
theResourceTarget = populateResourceMetadataRi(theEntitySource, tagList, theForHistoryOperation, res, theVersion);
|
||||||
|
}
|
||||||
|
return theResourceTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <R extends IResource> R populateResourceMetadataHapi(IBaseResourceEntity theEntity, @Nullable Collection<? extends BaseTag> theTagList, boolean theForHistoryOperation, R res, Long theVersion) {
|
||||||
|
R retVal = res;
|
||||||
|
if (theEntity.getDeleted() != null) {
|
||||||
|
res = (R) myContext.getResourceDefinition(res).newInstance();
|
||||||
|
retVal = res;
|
||||||
|
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
|
||||||
|
if (theForHistoryOperation) {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE);
|
||||||
|
}
|
||||||
|
} else if (theForHistoryOperation) {
|
||||||
|
/*
|
||||||
|
* If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
|
||||||
|
*/
|
||||||
|
Date published = theEntity.getPublished().getValue();
|
||||||
|
Date updated = theEntity.getUpdated().getValue();
|
||||||
|
if (published.equals(updated)) {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST);
|
||||||
|
} else {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.setId(theEntity.getIdDt().withVersion(theVersion.toString()));
|
||||||
|
|
||||||
|
ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion()));
|
||||||
|
ResourceMetadataKeyEnum.PUBLISHED.put(res, theEntity.getPublished());
|
||||||
|
ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated());
|
||||||
|
IDao.RESOURCE_PID.put(res, theEntity.getResourceId());
|
||||||
|
|
||||||
|
if (theTagList != null) {
|
||||||
|
if (theEntity.isHasTags()) {
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
List<IBaseCoding> securityLabels = new ArrayList<>();
|
||||||
|
List<IdDt> profiles = new ArrayList<>();
|
||||||
|
for (BaseTag next : theTagList) {
|
||||||
|
switch (next.getTag().getTagType()) {
|
||||||
|
case PROFILE:
|
||||||
|
profiles.add(new IdDt(next.getTag().getCode()));
|
||||||
|
break;
|
||||||
|
case SECURITY_LABEL:
|
||||||
|
IBaseCoding secLabel = (IBaseCoding) myContext.getVersion().newCodingDt();
|
||||||
|
secLabel.setSystem(next.getTag().getSystem());
|
||||||
|
secLabel.setCode(next.getTag().getCode());
|
||||||
|
secLabel.setDisplay(next.getTag().getDisplay());
|
||||||
|
securityLabels.add(secLabel);
|
||||||
|
break;
|
||||||
|
case TAG:
|
||||||
|
tagList.add(new Tag(next.getTag().getSystem(), next.getTag().getCode(), next.getTag().getDisplay()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tagList.size() > 0) {
|
||||||
|
ResourceMetadataKeyEnum.TAG_LIST.put(res, tagList);
|
||||||
|
}
|
||||||
|
if (securityLabels.size() > 0) {
|
||||||
|
ResourceMetadataKeyEnum.SECURITY_LABELS.put(res, toBaseCodingList(securityLabels));
|
||||||
|
}
|
||||||
|
if (profiles.size() > 0) {
|
||||||
|
ResourceMetadataKeyEnum.PROFILES.put(res, profiles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <R extends IBaseResource> R populateResourceMetadataRi(IBaseResourceEntity theEntity, @Nullable Collection<? extends BaseTag> theTagList, boolean theForHistoryOperation, IAnyResource res, Long theVersion) {
|
||||||
|
R retVal = (R) res;
|
||||||
|
if (theEntity.getDeleted() != null) {
|
||||||
|
res = (IAnyResource) myContext.getResourceDefinition(res).newInstance();
|
||||||
|
retVal = (R) res;
|
||||||
|
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
|
||||||
|
if (theForHistoryOperation) {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, Bundle.HTTPVerb.DELETE.toCode());
|
||||||
|
}
|
||||||
|
} else if (theForHistoryOperation) {
|
||||||
|
/*
|
||||||
|
* If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
|
||||||
|
*/
|
||||||
|
Date published = theEntity.getPublished().getValue();
|
||||||
|
Date updated = theEntity.getUpdated().getValue();
|
||||||
|
if (published.equals(updated)) {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, Bundle.HTTPVerb.POST.toCode());
|
||||||
|
} else {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, Bundle.HTTPVerb.PUT.toCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.getMeta().setLastUpdated(null);
|
||||||
|
res.getMeta().setVersionId(null);
|
||||||
|
|
||||||
|
updateResourceMetadata(theEntity, res);
|
||||||
|
res.setId(res.getIdElement().withVersion(theVersion.toString()));
|
||||||
|
|
||||||
|
res.getMeta().setLastUpdated(theEntity.getUpdatedDate());
|
||||||
|
IDao.RESOURCE_PID.put(res, theEntity.getResourceId());
|
||||||
|
|
||||||
|
if (theTagList != null) {
|
||||||
|
res.getMeta().getTag().clear();
|
||||||
|
res.getMeta().getProfile().clear();
|
||||||
|
res.getMeta().getSecurity().clear();
|
||||||
|
for (BaseTag next : theTagList) {
|
||||||
|
switch (next.getTag().getTagType()) {
|
||||||
|
case PROFILE:
|
||||||
|
res.getMeta().addProfile(next.getTag().getCode());
|
||||||
|
break;
|
||||||
|
case SECURITY_LABEL:
|
||||||
|
IBaseCoding sec = res.getMeta().addSecurity();
|
||||||
|
sec.setSystem(next.getTag().getSystem());
|
||||||
|
sec.setCode(next.getTag().getCode());
|
||||||
|
sec.setDisplay(next.getTag().getDisplay());
|
||||||
|
break;
|
||||||
|
case TAG:
|
||||||
|
IBaseCoding tag = res.getMeta().addTag();
|
||||||
|
tag.setSystem(next.getTag().getSystem());
|
||||||
|
tag.setCode(next.getTag().getCode());
|
||||||
|
tag.setDisplay(next.getTag().getDisplay());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateResourceMetadata(IBaseResourceEntity theEntitySource, IBaseResource theResourceTarget) {
|
||||||
|
IIdType id = theEntitySource.getIdDt();
|
||||||
|
if (myContext.getVersion().getVersion().isRi()) {
|
||||||
|
id = myContext.getVersion().newIdType().setValue(id.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id.hasResourceType() == false) {
|
||||||
|
id = id.withResourceType(theEntitySource.getResourceType());
|
||||||
|
}
|
||||||
|
|
||||||
|
theResourceTarget.setId(id);
|
||||||
|
if (theResourceTarget instanceof IResource) {
|
||||||
|
ResourceMetadataKeyEnum.VERSION.put((IResource) theResourceTarget, id.getVersionIdPart());
|
||||||
|
ResourceMetadataKeyEnum.UPDATED.put((IResource) theResourceTarget, theEntitySource.getUpdated());
|
||||||
|
} else {
|
||||||
|
IBaseMetaType meta = theResourceTarget.getMeta();
|
||||||
|
meta.setVersionId(id.getVersionIdPart());
|
||||||
|
meta.setLastUpdated(theEntitySource.getUpdatedDate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FhirContext getContext(FhirVersionEnum theVersion) {
|
||||||
|
Validate.notNull(theVersion, "theVersion must not be null");
|
||||||
|
if (theVersion == myContext.getVersion().getVersion()) {
|
||||||
|
return myContext;
|
||||||
|
}
|
||||||
|
return FhirContext.forCached(theVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String decodedResourceText(byte[] resourceBytes, String resourceText, ResourceEncodingEnum resourceEncoding) {
|
||||||
|
String decodedResourceText;
|
||||||
|
if (resourceText != null) {
|
||||||
|
decodedResourceText = resourceText;
|
||||||
|
} else {
|
||||||
|
decodedResourceText = decodeResource(resourceBytes, resourceEncoding);
|
||||||
|
}
|
||||||
|
return decodedResourceText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BaseCodingDt> toBaseCodingList(List<IBaseCoding> theSecurityLabels) {
|
||||||
|
ArrayList<BaseCodingDt> retVal = new ArrayList<>(theSecurityLabels.size());
|
||||||
|
for (IBaseCoding next : theSecurityLabels) {
|
||||||
|
retVal.add((BaseCodingDt) next);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,59 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
|
||||||
import org.hl7.fhir.dstu3.model.Composition;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class FhirResourceDaoCompositionDstu3 extends BaseHapiFhirResourceDao<Composition> implements IFhirResourceDaoComposition<Composition> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
|
|
||||||
SearchParameterMap paramMap = new SearchParameterMap();
|
|
||||||
if (theCount != null) {
|
|
||||||
paramMap.setCount(theCount.getValue());
|
|
||||||
}
|
|
||||||
if (theOffset != null) {
|
|
||||||
paramMap.setOffset(theOffset.getValue());
|
|
||||||
}
|
|
||||||
paramMap.setIncludes(Collections.singleton(IResource.INCLUDE_ALL.asRecursive()));
|
|
||||||
paramMap.setSort(theSort);
|
|
||||||
paramMap.setLastUpdated(theLastUpdate);
|
|
||||||
if (theId != null) {
|
|
||||||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
|
||||||
}
|
|
||||||
return search(paramMap, theRequestDetails);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation;
|
|
||||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
|
||||||
import org.hl7.fhir.dstu3.model.Observation;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class FhirResourceDaoObservationDstu3 extends BaseHapiFhirResourceDaoObservation<Observation> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IRequestPartitionHelperSvc myPartitionHelperSvc;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
|
||||||
|
|
||||||
updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails);
|
|
||||||
|
|
||||||
RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null);
|
|
||||||
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getEffectiveParamName() {
|
|
||||||
return Observation.SP_DATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getCodeParamName() {
|
|
||||||
return Observation.SP_CODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getSubjectParamName() {
|
|
||||||
return Observation.SP_SUBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getPatientParamName() {
|
|
||||||
return Observation.SP_PATIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
|
||||||
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
|
||||||
return updateObservationEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull,
|
|
||||||
thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate,
|
|
||||||
theCreateNewHistoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40;
|
|
||||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40;
|
|
||||||
import org.hl7.fhir.dstu3.model.PrimitiveType;
|
|
||||||
import org.hl7.fhir.dstu3.model.SearchParameter;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterDstu3 extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
|
||||||
|
|
||||||
protected void reindexAffectedResources(SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
|
||||||
String expression = theResource != null ? theResource.getExpression() : null;
|
|
||||||
List<String> base = theResource != null ? theResource.getBase().stream().map(PrimitiveType::asStringValue).collect(Collectors.toList()) : null;
|
|
||||||
requestReindexForRelatedResources(reindex, base, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postPersist(theEntity, theResource, theRequestDetails);
|
|
||||||
reindexAffectedResources(theResource, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postUpdate(theEntity, theResource, theRequestDetails);
|
|
||||||
reindexAffectedResources(theResource, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
|
||||||
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
|
|
||||||
reindexAffectedResources(theResourceToDelete, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
|
||||||
|
|
||||||
org.hl7.fhir.r4.model.SearchParameter resource = (org.hl7.fhir.r4.model.SearchParameter) VersionConvertorFactory_30_40.convertResource(theResource, new BaseAdvisor_30_40(false));
|
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(
|
|
||||||
resource,
|
|
||||||
getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -25,10 +25,10 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
|
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||||
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedComboStringUniqueDao;
|
||||||
|
@ -42,10 +42,10 @@ import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamStringDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamTokenDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamTokenDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceLinkDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryProvenanceDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;
|
import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao;
|
||||||
|
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
@ -124,11 +124,13 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MemoryCacheService myMemoryCacheService;
|
private MemoryCacheService myMemoryCacheService;
|
||||||
|
@Autowired
|
||||||
|
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ResourcePersistentId> findHistoricalVersionsOfNonDeletedResources(String theResourceName, ResourcePersistentId theResourceId, int theRemainingCount) {
|
public List<ResourcePersistentId> findHistoricalVersionsOfNonDeletedResources(String theResourceName, ResourcePersistentId theResourceId, int theRemainingCount) {
|
||||||
if(isEmptyQuery(theRemainingCount)){
|
if (isEmptyQuery(theRemainingCount)) {
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.EMPTY_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +158,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<ResourcePersistentId> findHistoricalVersionsOfDeletedResources(String theResourceName, ResourcePersistentId theResourceId, int theRemainingCount) {
|
public List<ResourcePersistentId> findHistoricalVersionsOfDeletedResources(String theResourceName, ResourcePersistentId theResourceId, int theRemainingCount) {
|
||||||
if(isEmptyQuery(theRemainingCount)){
|
if (isEmptyQuery(theRemainingCount)) {
|
||||||
return Collections.EMPTY_LIST;
|
return Collections.EMPTY_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +196,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
||||||
* be optimized, but expunge is hopefully not frequently called on busy servers
|
* be optimized, but expunge is hopefully not frequently called on busy servers
|
||||||
* so it shouldn't be too big a deal.
|
* so it shouldn't be too big a deal.
|
||||||
*/
|
*/
|
||||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
|
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||||
@Override
|
@Override
|
||||||
public void afterCommit() {
|
public void afterCommit() {
|
||||||
myMemoryCacheService.invalidateAllCaches();
|
myMemoryCacheService.invalidateAllCaches();
|
||||||
|
@ -222,8 +224,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
||||||
private void callHooks(RequestDetails theRequestDetails, AtomicInteger theRemainingCount, ResourceHistoryTable theVersion, IdDt theId) {
|
private void callHooks(RequestDetails theRequestDetails, AtomicInteger theRemainingCount, ResourceHistoryTable theVersion, IdDt theId) {
|
||||||
final AtomicInteger counter = new AtomicInteger();
|
final AtomicInteger counter = new AtomicInteger();
|
||||||
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) {
|
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) {
|
||||||
IFhirResourceDao<?> resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
|
IBaseResource resource = myJpaStorageResourceParser.toResource(theVersion, false);
|
||||||
IBaseResource resource = resourceDao.toResource(theVersion, false);
|
|
||||||
HookParams params = new HookParams()
|
HookParams params = new HookParams()
|
||||||
.add(AtomicInteger.class, counter)
|
.add(AtomicInteger.class, counter)
|
||||||
.add(IIdType.class, theId)
|
.add(IIdType.class, theId)
|
||||||
|
@ -327,7 +328,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
||||||
|
|
||||||
private void expungeHistoricalVersionsOfId(RequestDetails theRequestDetails, Long myResourceId, AtomicInteger theRemainingCount) {
|
private void expungeHistoricalVersionsOfId(RequestDetails theRequestDetails, Long myResourceId, AtomicInteger theRemainingCount) {
|
||||||
Pageable page;
|
Pageable page;
|
||||||
synchronized (theRemainingCount){
|
synchronized (theRemainingCount) {
|
||||||
if (expungeLimitReached(theRemainingCount)) {
|
if (expungeLimitReached(theRemainingCount)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -351,7 +352,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
||||||
return new SliceImpl<>(Collections.singletonList(myVersion.getId()));
|
return new SliceImpl<>(Collections.singletonList(myVersion.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEmptyQuery(int theCount){
|
private boolean isEmptyQuery(int theCount) {
|
||||||
return theCount <= 0;
|
return theCount <= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation;
|
|
||||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.r4.model.Observation;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
import javax.persistence.PersistenceContext;
|
|
||||||
import javax.persistence.PersistenceContextType;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class FhirResourceDaoObservationR4 extends BaseHapiFhirResourceDaoObservation<Observation> {
|
|
||||||
|
|
||||||
|
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
|
||||||
protected EntityManager myEntityManager;
|
|
||||||
@Autowired
|
|
||||||
private IRequestPartitionHelperSvc myRequestPartitionHelperService;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
|
||||||
updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails);
|
|
||||||
|
|
||||||
RequestPartitionId requestPartitionId = myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null);
|
|
||||||
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getEffectiveParamName() {
|
|
||||||
return Observation.SP_DATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getCodeParamName() {
|
|
||||||
return Observation.SP_CODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getSubjectParamName() {
|
|
||||||
return Observation.SP_SUBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getPatientParamName() {
|
|
||||||
return Observation.SP_PATIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
|
||||||
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
|
||||||
return updateObservationEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull,
|
|
||||||
thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate,
|
|
||||||
theCreateNewHistoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4b;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
import org.hl7.fhir.r4b.model.Composition;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class FhirResourceDaoCompositionR4B extends BaseHapiFhirResourceDao<Composition> implements IFhirResourceDaoComposition<Composition> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
|
|
||||||
SearchParameterMap paramMap = new SearchParameterMap();
|
|
||||||
if (theCount != null) {
|
|
||||||
paramMap.setCount(theCount.getValue());
|
|
||||||
}
|
|
||||||
if (theOffset != null) {
|
|
||||||
paramMap.setOffset(theOffset.getValue());
|
|
||||||
}
|
|
||||||
paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive()));
|
|
||||||
paramMap.setSort(theSort);
|
|
||||||
paramMap.setLastUpdated(theLastUpdate);
|
|
||||||
if (theId != null) {
|
|
||||||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
|
||||||
}
|
|
||||||
return search(paramMap, theRequestDetails);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4b;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation;
|
|
||||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.r4b.model.Observation;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class FhirResourceDaoObservationR4B extends BaseHapiFhirResourceDaoObservation<Observation> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IRequestPartitionHelperSvc myPartitionHelperSvc;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
|
||||||
|
|
||||||
updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails);
|
|
||||||
|
|
||||||
RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null);
|
|
||||||
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getEffectiveParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_DATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getCodeParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_CODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getSubjectParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_SUBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getPatientParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_PATIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
|
||||||
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
|
||||||
return updateObservationEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull,
|
|
||||||
thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate,
|
|
||||||
theCreateNewHistoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4b;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
|
|
||||||
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_43_50;
|
|
||||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
|
|
||||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50;
|
|
||||||
import org.hl7.fhir.r4b.model.CodeType;
|
|
||||||
import org.hl7.fhir.r4b.model.SearchParameter;
|
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterR4B extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
|
||||||
|
|
||||||
protected void refactorAffectedResources(SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
|
||||||
String expression = theResource != null ? theResource.getExpression() : null;
|
|
||||||
List<String> base = theResource != null ? theResource.getBase().stream().map(CodeType::getCode).collect(Collectors.toList()) : null;
|
|
||||||
requestReindexForRelatedResources(reindex, base, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postPersist(theEntity, theResource, theRequestDetails);
|
|
||||||
refactorAffectedResources(theResource, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postUpdate(theEntity, theResource, theRequestDetails);
|
|
||||||
refactorAffectedResources(theResource, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
|
||||||
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
|
|
||||||
refactorAffectedResources(theResourceToDelete, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
|
||||||
|
|
||||||
Resource resR5 = VersionConvertorFactory_43_50.convertResource(theResource, new BaseAdvisor_43_50(false));
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(
|
|
||||||
(org.hl7.fhir.r4.model.SearchParameter) VersionConvertorFactory_40_50.convertResource(resR5, new BaseAdvisor_40_50(false)),
|
|
||||||
getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r5;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoComposition;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
import org.hl7.fhir.r5.model.Composition;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class FhirResourceDaoCompositionR5 extends BaseHapiFhirResourceDao<Composition> implements IFhirResourceDaoComposition<Composition> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider getDocumentForComposition(HttpServletRequest theServletRequest, IIdType theId, IPrimitiveType<Integer> theCount, IPrimitiveType<Integer> theOffset, DateRangeParam theLastUpdate, SortSpec theSort, RequestDetails theRequestDetails) {
|
|
||||||
SearchParameterMap paramMap = new SearchParameterMap();
|
|
||||||
if (theCount != null) {
|
|
||||||
paramMap.setCount(theCount.getValue());
|
|
||||||
}
|
|
||||||
if (theOffset != null) {
|
|
||||||
paramMap.setOffset(theOffset.getValue());
|
|
||||||
}
|
|
||||||
paramMap.setIncludes(Collections.singleton(IBaseResource.INCLUDE_ALL.asRecursive()));
|
|
||||||
paramMap.setSort(theSort);
|
|
||||||
paramMap.setLastUpdated(theLastUpdate);
|
|
||||||
if (theId != null) {
|
|
||||||
paramMap.add("_id", new StringParam(theId.getIdPart()));
|
|
||||||
}
|
|
||||||
return search(paramMap, theRequestDetails);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r5;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDaoObservation;
|
|
||||||
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.r5.model.Observation;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class FhirResourceDaoObservationR5 extends BaseHapiFhirResourceDaoObservation<Observation> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private IRequestPartitionHelperSvc myPartitionHelperSvc;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBundleProvider observationsLastN(SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) {
|
|
||||||
|
|
||||||
updateSearchParamsForLastn(theSearchParameterMap, theRequestDetails);
|
|
||||||
|
|
||||||
RequestPartitionId requestPartitionId = myPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, getResourceName(), theSearchParameterMap, null);
|
|
||||||
return mySearchCoordinatorSvc.registerSearch(this, theSearchParameterMap, getResourceName(), new CacheControlDirective().parse(theRequestDetails.getHeaders(Constants.HEADER_CACHE_CONTROL)), theRequestDetails, requestPartitionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getEffectiveParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_DATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getCodeParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_CODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getSubjectParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_SUBJECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getPatientParamName() {
|
|
||||||
return org.hl7.fhir.r4.model.Observation.SP_PATIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
|
||||||
boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
|
|
||||||
return updateObservationEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull,
|
|
||||||
thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate,
|
|
||||||
theCreateNewHistoryEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r5;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter;
|
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoSearchParameterR4;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
|
|
||||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
|
|
||||||
import org.hl7.fhir.r5.model.CodeType;
|
|
||||||
import org.hl7.fhir.r5.model.SearchParameter;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2022 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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class FhirResourceDaoSearchParameterR5 extends BaseHapiFhirResourceDao<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ISearchParamExtractor mySearchParamExtractor;
|
|
||||||
|
|
||||||
protected void refactorAffectedResources(SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
|
|
||||||
String expression = theResource != null ? theResource.getExpression() : null;
|
|
||||||
List<String> base = theResource != null ? theResource.getBase().stream().map(CodeType::getCode).collect(Collectors.toList()) : null;
|
|
||||||
requestReindexForRelatedResources(reindex, base, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postPersist(theEntity, theResource, theRequestDetails);
|
|
||||||
refactorAffectedResources(theResource, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
|
||||||
super.postUpdate(theEntity, theResource, theRequestDetails);
|
|
||||||
refactorAffectedResources(theResource, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
|
||||||
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
|
|
||||||
refactorAffectedResources(theResourceToDelete, theRequestDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
|
||||||
super.validateResourceForStorage(theResource, theEntityToSave);
|
|
||||||
|
|
||||||
FhirResourceDaoSearchParameterR4.validateSearchParam(
|
|
||||||
(org.hl7.fhir.r4.model.SearchParameter) VersionConvertorFactory_40_50.convertResource(theResource, new BaseAdvisor_40_50(false)),
|
|
||||||
getContext(), getConfig(), mySearchParamRegistry, mySearchParamExtractor);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -42,7 +43,7 @@ public class DeleteExpungeSvcImpl implements IDeleteExpungeSvc {
|
||||||
private final DeleteExpungeSqlBuilder myDeleteExpungeSqlBuilder;
|
private final DeleteExpungeSqlBuilder myDeleteExpungeSqlBuilder;
|
||||||
private final IFulltextSearchSvc myFullTextSearchSvc;
|
private final IFulltextSearchSvc myFullTextSearchSvc;
|
||||||
|
|
||||||
public DeleteExpungeSvcImpl(EntityManager theEntityManager, DeleteExpungeSqlBuilder theDeleteExpungeSqlBuilder, IFulltextSearchSvc theFullTextSearchSvc) {
|
public DeleteExpungeSvcImpl(EntityManager theEntityManager, DeleteExpungeSqlBuilder theDeleteExpungeSqlBuilder, @Autowired(required = false) IFulltextSearchSvc theFullTextSearchSvc) {
|
||||||
myEntityManager = theEntityManager;
|
myEntityManager = theEntityManager;
|
||||||
myDeleteExpungeSqlBuilder = theDeleteExpungeSqlBuilder;
|
myDeleteExpungeSqlBuilder = theDeleteExpungeSqlBuilder;
|
||||||
myFullTextSearchSvc = theFullTextSearchSvc;
|
myFullTextSearchSvc = theFullTextSearchSvc;
|
||||||
|
|
|
@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.HistoryBuilder;
|
import ca.uhn.fhir.jpa.dao.HistoryBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
|
import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
|
@ -104,13 +105,15 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
private RequestPartitionHelperSvc myRequestPartitionHelperSvc;
|
private RequestPartitionHelperSvc myRequestPartitionHelperSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoConfig myDaoConfig;
|
private DaoConfig myDaoConfig;
|
||||||
|
@Autowired
|
||||||
|
private MemoryCacheService myMemoryCacheService;
|
||||||
|
@Autowired
|
||||||
|
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non autowired fields (will be different for every instance
|
* Non autowired fields (will be different for every instance
|
||||||
* of this class, since it's a prototype
|
* of this class, since it's a prototype
|
||||||
*/
|
*/
|
||||||
@Autowired
|
|
||||||
private MemoryCacheService myMemoryCacheService;
|
|
||||||
private Search mySearchEntity;
|
private Search mySearchEntity;
|
||||||
private String myUuid;
|
private String myUuid;
|
||||||
private SearchCacheStatusEnum myCacheStatus;
|
private SearchCacheStatusEnum myCacheStatus;
|
||||||
|
@ -162,7 +165,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
||||||
resource = next;
|
resource = next;
|
||||||
|
|
||||||
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(next.getResourceType());
|
IFhirResourceDao<?> dao = myDaoRegistry.getResourceDao(next.getResourceType());
|
||||||
retVal.add(dao.toResource(resource, true));
|
retVal.add(myJpaStorageResourceParser.toResource(resource, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,20 @@ package ca.uhn.fhir.jpa.search;
|
||||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
|
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||||
|
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||||
import ca.uhn.fhir.jpa.search.builder.tasks.SearchTask;
|
import ca.uhn.fhir.jpa.search.builder.tasks.SearchTask;
|
||||||
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.param.HistorySearchStyleEnum;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||||
|
|
||||||
public class PersistedJpaBundleProviderFactory {
|
public class PersistedJpaBundleProviderFactory {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -46,4 +55,28 @@ public class PersistedJpaBundleProviderFactory {
|
||||||
public PersistedJpaSearchFirstPageBundleProvider newInstanceFirstPage(RequestDetails theRequestDetails, Search theSearch, SearchTask theTask, ISearchBuilder theSearchBuilder) {
|
public PersistedJpaSearchFirstPageBundleProvider newInstanceFirstPage(RequestDetails theRequestDetails, Search theSearch, SearchTask theTask, ISearchBuilder theSearchBuilder) {
|
||||||
return (PersistedJpaSearchFirstPageBundleProvider) myApplicationContext.getBean(JpaConfig.PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER, theRequestDetails, theSearch, theTask, theSearchBuilder);
|
return (PersistedJpaSearchFirstPageBundleProvider) myApplicationContext.getBean(JpaConfig.PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER, theRequestDetails, theSearch, theTask, theSearchBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IBundleProvider history(RequestDetails theRequest, String theResourceType, Long theResourcePid, Date theRangeStartInclusive, Date theRangeEndInclusive, Integer theOffset) {
|
||||||
|
return history(theRequest, theResourceType, theResourcePid, theRangeStartInclusive, theRangeEndInclusive, theOffset, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBundleProvider history(RequestDetails theRequest, String theResourceType, Long theResourcePid, Date theRangeStartInclusive, Date theRangeEndInclusive, Integer theOffset, HistorySearchStyleEnum searchParameterType) {
|
||||||
|
String resourceName = defaultIfBlank(theResourceType, null);
|
||||||
|
|
||||||
|
Search search = new Search();
|
||||||
|
search.setOffset(theOffset);
|
||||||
|
search.setDeleted(false);
|
||||||
|
search.setCreated(new Date());
|
||||||
|
search.setLastUpdated(theRangeStartInclusive, theRangeEndInclusive);
|
||||||
|
search.setUuid(UUID.randomUUID().toString());
|
||||||
|
search.setResourceType(resourceName);
|
||||||
|
search.setResourceId(theResourcePid);
|
||||||
|
search.setSearchType(SearchTypeEnum.HISTORY);
|
||||||
|
search.setStatus(SearchStatusEnum.FINISHED);
|
||||||
|
search.setHistorySearchStyle(searchParameterType);
|
||||||
|
|
||||||
|
return newInstance(theRequest, search);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,15 +39,16 @@ import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean;
|
||||||
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
|
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseStorageDao;
|
import ca.uhn.fhir.jpa.dao.BaseStorageDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser;
|
||||||
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
||||||
import ca.uhn.fhir.jpa.dao.search.ResourceNotFoundInIndexException;
|
import ca.uhn.fhir.jpa.dao.search.ResourceNotFoundInIndexException;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
|
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||||
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity;
|
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||||
|
@ -145,20 +146,34 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static final int MAXIMUM_PAGE_SIZE = SearchConstants.MAX_PAGE_SIZE;
|
public static final int MAXIMUM_PAGE_SIZE = SearchConstants.MAX_PAGE_SIZE;
|
||||||
public static final int MAXIMUM_PAGE_SIZE_FOR_TESTING = 50;
|
public static final int MAXIMUM_PAGE_SIZE_FOR_TESTING = 50;
|
||||||
|
public static final String RESOURCE_ID_ALIAS = "resource_id";
|
||||||
|
public static final String RESOURCE_VERSION_ALIAS = "resource_version";
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(SearchBuilder.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(SearchBuilder.class);
|
||||||
private static final JpaPid NO_MORE = new JpaPid(-1L);
|
private static final JpaPid NO_MORE = new JpaPid(-1L);
|
||||||
private static final String MY_TARGET_RESOURCE_PID = "myTargetResourcePid";
|
private static final String MY_TARGET_RESOURCE_PID = "myTargetResourcePid";
|
||||||
private static final String MY_SOURCE_RESOURCE_PID = "mySourceResourcePid";
|
private static final String MY_SOURCE_RESOURCE_PID = "mySourceResourcePid";
|
||||||
private static final String MY_TARGET_RESOURCE_TYPE = "myTargetResourceType";
|
private static final String MY_TARGET_RESOURCE_TYPE = "myTargetResourceType";
|
||||||
|
|
||||||
private static final String MY_SOURCE_RESOURCE_TYPE = "mySourceResourceType";
|
private static final String MY_SOURCE_RESOURCE_TYPE = "mySourceResourceType";
|
||||||
private static final String MY_TARGET_RESOURCE_VERSION = "myTargetResourceVersion";
|
private static final String MY_TARGET_RESOURCE_VERSION = "myTargetResourceVersion";
|
||||||
public static final String RESOURCE_ID_ALIAS = "resource_id";
|
|
||||||
public static final String RESOURCE_VERSION_ALIAS = "resource_version";
|
|
||||||
public static boolean myUseMaxPageSize50ForTest = false;
|
public static boolean myUseMaxPageSize50ForTest = false;
|
||||||
|
protected final IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||||
|
protected final IResourceTagDao myResourceTagDao;
|
||||||
private final String myResourceName;
|
private final String myResourceName;
|
||||||
private final Class<? extends IBaseResource> myResourceType;
|
private final Class<? extends IBaseResource> myResourceType;
|
||||||
|
private final HapiFhirLocalContainerEntityManagerFactoryBean myEntityManagerFactory;
|
||||||
|
private final SqlObjectFactory mySqlBuilderFactory;
|
||||||
|
private final HibernatePropertiesProvider myDialectProvider;
|
||||||
|
private final ModelConfig myModelConfig;
|
||||||
|
private final ISearchParamRegistry mySearchParamRegistry;
|
||||||
|
private final PartitionSettings myPartitionSettings;
|
||||||
|
private final DaoRegistry myDaoRegistry;
|
||||||
|
private final IResourceSearchViewDao myResourceSearchViewDao;
|
||||||
|
private final FhirContext myContext;
|
||||||
|
private final IIdHelperService myIdHelperService;
|
||||||
|
private final DaoConfig myDaoConfig;
|
||||||
|
private final IDao myCallingDao;
|
||||||
|
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||||
|
protected EntityManager myEntityManager;
|
||||||
private List<ResourcePersistentId> myAlsoIncludePids;
|
private List<ResourcePersistentId> myAlsoIncludePids;
|
||||||
private CriteriaBuilder myCriteriaBuilder;
|
private CriteriaBuilder myCriteriaBuilder;
|
||||||
private SearchParameterMap myParams;
|
private SearchParameterMap myParams;
|
||||||
|
@ -168,30 +183,12 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
private Set<ResourcePersistentId> myPidSet;
|
private Set<ResourcePersistentId> myPidSet;
|
||||||
private boolean myHasNextIteratorQuery = false;
|
private boolean myHasNextIteratorQuery = false;
|
||||||
private RequestPartitionId myRequestPartitionId;
|
private RequestPartitionId myRequestPartitionId;
|
||||||
|
|
||||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
|
||||||
protected EntityManager myEntityManager;
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private IFulltextSearchSvc myFulltextSearchSvc;
|
private IFulltextSearchSvc myFulltextSearchSvc;
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private IElasticsearchSvc myIElasticsearchSvc;
|
private IElasticsearchSvc myIElasticsearchSvc;
|
||||||
|
@Autowired
|
||||||
private final HapiFhirLocalContainerEntityManagerFactoryBean myEntityManagerFactory;
|
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||||
private final SqlObjectFactory mySqlBuilderFactory;
|
|
||||||
private final HibernatePropertiesProvider myDialectProvider;
|
|
||||||
private final ModelConfig myModelConfig;
|
|
||||||
private final ISearchParamRegistry mySearchParamRegistry;
|
|
||||||
private final PartitionSettings myPartitionSettings;
|
|
||||||
protected final IInterceptorBroadcaster myInterceptorBroadcaster;
|
|
||||||
protected final IResourceTagDao myResourceTagDao;
|
|
||||||
private final DaoRegistry myDaoRegistry;
|
|
||||||
private final IResourceSearchViewDao myResourceSearchViewDao;
|
|
||||||
private final FhirContext myContext;
|
|
||||||
private final IIdHelperService myIdHelperService;
|
|
||||||
|
|
||||||
private final DaoConfig myDaoConfig;
|
|
||||||
|
|
||||||
private final IDao myCallingDao;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -894,7 +891,7 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
|
|
||||||
IBaseResource resource = null;
|
IBaseResource resource = null;
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
resource = myCallingDao.toResource(resourceType, next, tagMap.get(next.getId()), theForHistoryOperation);
|
resource = myJpaStorageResourceParser.toResource(resourceType, next, tagMap.get(next.getId()), theForHistoryOperation);
|
||||||
}
|
}
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
ourLog.warn("Unable to find resource {}/{}/_history/{} in database", next.getResourceType(), next.getIdDt().getIdPart(), next.getVersion());
|
ourLog.warn("Unable to find resource {}/{}/_history/{} in database", next.getResourceType(), next.getIdDt().getIdPart(), next.getVersion());
|
||||||
|
|
|
@ -38,7 +38,7 @@ import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
|
||||||
import ca.uhn.fhir.jpa.config.util.ConnectionPoolInfoProvider;
|
import ca.uhn.fhir.jpa.config.util.ConnectionPoolInfoProvider;
|
||||||
import ca.uhn.fhir.jpa.config.util.IConnectionPoolInfoProvider;
|
import ca.uhn.fhir.jpa.config.util.IConnectionPoolInfoProvider;
|
||||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
|
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
|
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
|
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
|
||||||
|
@ -61,6 +61,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptPropertyTypeEnum;
|
||||||
import ca.uhn.fhir.jpa.entity.TermValueSet;
|
import ca.uhn.fhir.jpa.entity.TermValueSet;
|
||||||
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
|
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
|
||||||
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
|
import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum;
|
||||||
|
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
||||||
|
@ -265,6 +266,8 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
||||||
private CachingValidationSupport myCachingValidationSupport;
|
private CachingValidationSupport myCachingValidationSupport;
|
||||||
@Autowired
|
@Autowired
|
||||||
private VersionCanonicalizer myVersionCanonicalizer;
|
private VersionCanonicalizer myVersionCanonicalizer;
|
||||||
|
@Autowired
|
||||||
|
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||||
|
@ -2437,7 +2440,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
||||||
+ ForcedId.IDX_FORCEDID_TYPE_FID + " removed?");
|
+ ForcedId.IDX_FORCEDID_TYPE_FID + " removed?");
|
||||||
|
|
||||||
IFhirResourceDao<CodeSystem> csDao = myDaoRegistry.getResourceDao("CodeSystem");
|
IFhirResourceDao<CodeSystem> csDao = myDaoRegistry.getResourceDao("CodeSystem");
|
||||||
IBaseResource cs = csDao.toResource(resultList.get(0), false);
|
IBaseResource cs = myJpaStorageResourceParser.toResource(resultList.get(0), false);
|
||||||
return Optional.of(cs);
|
return Optional.of(cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2526,7 +2529,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
||||||
|
|
||||||
private org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) {
|
private org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) {
|
||||||
Class<? extends IBaseResource> type = getFhirContext().getResourceDefinition("ValueSet").getImplementingClass();
|
Class<? extends IBaseResource> type = getFhirContext().getResourceDefinition("ValueSet").getImplementingClass();
|
||||||
IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").toResource(type, theResourceTable, null, false);
|
IBaseResource valueSet = myJpaStorageResourceParser.toResource(type, theResourceTable, null, false);
|
||||||
return myVersionCanonicalizer.valueSetToCanonical(valueSet);
|
return myVersionCanonicalizer.valueSetToCanonical(valueSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -44,12 +44,6 @@ public class BaseCqlR4Test extends BaseJpaR4Test implements CqlProviderTestBase
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
protected PartitionHelper myPartitionHelper;
|
protected PartitionHelper myPartitionHelper;
|
||||||
|
|
||||||
// FIXME: restore?
|
|
||||||
// @Override
|
|
||||||
// public void beforeResetInterceptors() {
|
|
||||||
// myInterceptorRegistry.unregisterInterceptorsIf(t->!(t instanceof PartitionHelper.MyTestInterceptor));
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected
|
protected
|
||||||
DaoRegistry myDaoRegistry;
|
DaoRegistry myDaoRegistry;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -20,14 +20,15 @@ package ca.uhn.fhir.jpa.model.cross;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public interface IBasePersistedResource extends IResourceLookup {
|
public interface IBasePersistedResource extends IResourceLookup {
|
||||||
|
|
||||||
IIdType getIdDt();
|
IIdType getIdDt();
|
||||||
|
|
||||||
|
long getVersion();
|
||||||
|
|
||||||
|
boolean isDeleted();
|
||||||
|
|
||||||
|
void setNotDeleted();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,10 @@ package ca.uhn.fhir.jpa.model.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.context.ParserOptions;
|
import ca.uhn.fhir.context.ParserOptions;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.dstu2.model.Subscription;
|
import org.hl7.fhir.dstu2.model.Subscription;
|
||||||
|
@ -79,6 +80,30 @@ public class ModelConfig {
|
||||||
private boolean myDefaultSearchParamsCanBeOverridden = true;
|
private boolean myDefaultSearchParamsCanBeOverridden = true;
|
||||||
private Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>();
|
private Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>();
|
||||||
private boolean myCrossPartitionSubscription = false;
|
private boolean myCrossPartitionSubscription = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, attempt to map terminology for bulk export jobs using the
|
||||||
|
* logic in
|
||||||
|
* {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>.
|
||||||
|
*
|
||||||
|
* @since 6.3.0
|
||||||
|
*/
|
||||||
|
public boolean isNormalizeTerminologyForBulkExportJobs() {
|
||||||
|
return myNormalizeTerminologyForBulkExportJobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, attempt to map terminology for bulk export jobs using the
|
||||||
|
* logic in
|
||||||
|
* {@link ResponseTerminologyTranslationSvc}. Default is <code>false</code>.
|
||||||
|
*
|
||||||
|
* @since 6.3.0
|
||||||
|
*/
|
||||||
|
public void setNormalizeTerminologyForBulkExportJobs(boolean theNormalizeTerminologyForBulkExportJobs) {
|
||||||
|
myNormalizeTerminologyForBulkExportJobs = theNormalizeTerminologyForBulkExportJobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean myNormalizeTerminologyForBulkExportJobs = false;
|
||||||
private String myEmailFromAddress = "noreply@unknown.com";
|
private String myEmailFromAddress = "noreply@unknown.com";
|
||||||
private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH;
|
private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH;
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -213,6 +213,16 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
||||||
return myResourceVersion;
|
return myResourceVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return getDeleted() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNotDeleted() {
|
||||||
|
setDeleted(null);
|
||||||
|
}
|
||||||
|
|
||||||
public void setVersion(long theVersion) {
|
public void setVersion(long theVersion) {
|
||||||
myResourceVersion = theVersion;
|
myResourceVersion = theVersion;
|
||||||
}
|
}
|
||||||
|
|
|
@ -549,6 +549,16 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
|
||||||
return myVersion;
|
return myVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDeleted() {
|
||||||
|
return getDeleted() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNotDeleted() {
|
||||||
|
setDeleted(null);
|
||||||
|
}
|
||||||
|
|
||||||
public void setVersion(long theVersion) {
|
public void setVersion(long theVersion) {
|
||||||
myVersion = theVersion;
|
myVersion = theVersion;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.searchparam.registry;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.context.ComboSearchParamType;
|
import ca.uhn.fhir.context.ComboSearchParamType;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
|
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
@ -179,33 +179,35 @@ public class SearchParameterCanonicalizer {
|
||||||
String path = theNextSp.getExpression();
|
String path = theNextSp.getExpression();
|
||||||
RestSearchParameterTypeEnum paramType = null;
|
RestSearchParameterTypeEnum paramType = null;
|
||||||
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
|
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
|
||||||
switch (theNextSp.getType()) {
|
if (theNextSp.getType() != null) {
|
||||||
case COMPOSITE:
|
switch (theNextSp.getType()) {
|
||||||
paramType = RestSearchParameterTypeEnum.COMPOSITE;
|
case COMPOSITE:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.COMPOSITE;
|
||||||
case DATE:
|
break;
|
||||||
paramType = RestSearchParameterTypeEnum.DATE;
|
case DATE:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.DATE;
|
||||||
case NUMBER:
|
break;
|
||||||
paramType = RestSearchParameterTypeEnum.NUMBER;
|
case NUMBER:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.NUMBER;
|
||||||
case QUANTITY:
|
break;
|
||||||
paramType = RestSearchParameterTypeEnum.QUANTITY;
|
case QUANTITY:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.QUANTITY;
|
||||||
case REFERENCE:
|
break;
|
||||||
paramType = RestSearchParameterTypeEnum.REFERENCE;
|
case REFERENCE:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.REFERENCE;
|
||||||
case STRING:
|
break;
|
||||||
paramType = RestSearchParameterTypeEnum.STRING;
|
case STRING:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.STRING;
|
||||||
case TOKEN:
|
break;
|
||||||
paramType = RestSearchParameterTypeEnum.TOKEN;
|
case TOKEN:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.TOKEN;
|
||||||
case URI:
|
break;
|
||||||
paramType = RestSearchParameterTypeEnum.URI;
|
case URI:
|
||||||
break;
|
paramType = RestSearchParameterTypeEnum.URI;
|
||||||
case NULL:
|
break;
|
||||||
break;
|
case NULL:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (theNextSp.getStatus() != null) {
|
if (theNextSp.getStatus() != null) {
|
||||||
switch (theNextSp.getStatus()) {
|
switch (theNextSp.getStatus()) {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
|
@ -94,7 +95,12 @@ public class SubscriptionValidatingInterceptor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CanonicalSubscription subscription = mySubscriptionCanonicalizer.canonicalize(theSubscription);
|
CanonicalSubscription subscription;
|
||||||
|
try {
|
||||||
|
subscription = mySubscriptionCanonicalizer.canonicalize(theSubscription);
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
throw new UnprocessableEntityException(Msg.code(955) + e.getMessage());
|
||||||
|
}
|
||||||
boolean finished = false;
|
boolean finished = false;
|
||||||
if (subscription.getStatus() == null) {
|
if (subscription.getStatus() == null) {
|
||||||
throw new UnprocessableEntityException(Msg.code(8) + "Can not process submitted Subscription - Subscription.status must be populated on this server");
|
throw new UnprocessableEntityException(Msg.code(8) + "Can not process submitted Subscription - Subscription.status must be populated on this server");
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController;
|
||||||
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
|
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
|
||||||
import ca.uhn.fhir.jpa.test.BaseJpaTest;
|
import ca.uhn.fhir.jpa.test.BaseJpaTest;
|
||||||
|
import ca.uhn.fhir.jpa.test.PreventDanglingInterceptorsExtension;
|
||||||
import ca.uhn.fhir.jpa.test.config.TestDstu2Config;
|
import ca.uhn.fhir.jpa.test.config.TestDstu2Config;
|
||||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||||
|
@ -60,6 +61,7 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
@ -213,6 +215,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ValidationSupportChain myJpaValidationSupportChain;
|
private ValidationSupportChain myJpaValidationSupportChain;
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
private final PreventDanglingInterceptorsExtension myPreventDanglingInterceptorsExtension = new PreventDanglingInterceptorsExtension(()-> myInterceptorRegistry);
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void beforeFlushFT() {
|
public void beforeFlushFT() {
|
||||||
purgeHibernateSearch(myEntityManager);
|
purgeHibernateSearch(myEntityManager);
|
||||||
|
@ -232,13 +237,6 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
||||||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@AfterEach
|
|
||||||
public void afterResetInterceptors() {
|
|
||||||
super.afterResetInterceptors();
|
|
||||||
myInterceptorRegistry.unregisterAllInterceptors();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FhirContext getFhirContext() {
|
public FhirContext getFhirContext() {
|
||||||
return myFhirContext;
|
return myFhirContext;
|
||||||
|
|
|
@ -58,6 +58,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
Subscription subs = new Subscription();
|
Subscription subs = new Subscription();
|
||||||
subs.getChannel().setType(SubscriptionChannelTypeEnum.REST_HOOK);
|
subs.getChannel().setType(SubscriptionChannelTypeEnum.REST_HOOK);
|
||||||
subs.setCriteria("Observation?identifier=123");
|
subs.setCriteria("Observation?identifier=123");
|
||||||
|
subs.getChannel().setEndpoint("http://localhost");
|
||||||
try {
|
try {
|
||||||
myClient.create().resource(subs).execute();
|
myClient.create().resource(subs).execute();
|
||||||
fail();
|
fail();
|
||||||
|
@ -105,6 +106,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
public void testCreateWithPopulatedButInvalidStatue() {
|
public void testCreateWithPopulatedButInvalidStatue() {
|
||||||
Subscription subs = new Subscription();
|
Subscription subs = new Subscription();
|
||||||
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||||
|
subs.getChannel().setEndpoint("http://localhost");
|
||||||
subs.setCriteria("Observation?identifier=123");
|
subs.setCriteria("Observation?identifier=123");
|
||||||
subs.getStatusElement().setValue("aaaaa");
|
subs.getStatusElement().setValue("aaaaa");
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
myClient.create().resource(subs).execute();
|
myClient.create().resource(subs).execute();
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertThat(e.getMessage(), containsString("Subscription.status must be populated on this server"));
|
assertThat(e.getMessage(), containsString("Unknown SubscriptionStatus code 'aaaaa'"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +154,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
||||||
Subscription subs = new Subscription();
|
Subscription subs = new Subscription();
|
||||||
subs.getChannel().setType(SubscriptionChannelTypeEnum.REST_HOOK);
|
subs.getChannel().setType(SubscriptionChannelTypeEnum.REST_HOOK);
|
||||||
subs.setCriteria("Observation?identifier=123");
|
subs.setCriteria("Observation?identifier=123");
|
||||||
|
subs.getChannel().setEndpoint("http://localhost");
|
||||||
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
|
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
|
||||||
IIdType id = myClient.create().resource(subs).execute().getId();
|
IIdType id = myClient.create().resource(subs).execute().getId();
|
||||||
subs.setId(id);
|
subs.setId(id);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateSpWithMultiplePaths(){
|
public void testCreateSpWithMultiplePaths() {
|
||||||
SearchParameter sp = new SearchParameter();
|
SearchParameter sp = new SearchParameter();
|
||||||
sp.setCode("telephone-unformatted");
|
sp.setCode("telephone-unformatted");
|
||||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
@ -101,18 +101,56 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
sp.getBase().add(new CodeType("Patient"));
|
sp.getBase().add(new CodeType("Patient"));
|
||||||
sp.setType(Enumerations.SearchParamType.TOKEN);
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
|
||||||
DaoMethodOutcome daoMethodOutcome = mySearchParameterDao.create(sp);
|
DaoMethodOutcome daoMethodOutcome;
|
||||||
|
|
||||||
|
daoMethodOutcome = mySearchParameterDao.create(sp);
|
||||||
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateSpWithMultiplePaths2() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setCode("telephone-unformatted");
|
||||||
|
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
sp.setExpression("Patient.telecom.where(system='phone' or system='email')");
|
||||||
|
sp.getBase().add(new CodeType("Patient"));
|
||||||
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
|
||||||
|
DaoMethodOutcome daoMethodOutcome;
|
||||||
|
|
||||||
sp.setExpression("Patient.telecom.where(system='phone') or Patient.telecome.where(system='email')");
|
sp.setExpression("Patient.telecom.where(system='phone') or Patient.telecome.where(system='email')");
|
||||||
sp.setCode("telephone-unformatted-2");
|
sp.setCode("telephone-unformatted-2");
|
||||||
daoMethodOutcome = mySearchParameterDao.create(sp);
|
daoMethodOutcome = mySearchParameterDao.create(sp);
|
||||||
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateSpWithMultiplePaths3() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setCode("telephone-unformatted");
|
||||||
|
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
sp.setExpression("Patient.telecom.where(system='phone' or system='email')");
|
||||||
|
sp.getBase().add(new CodeType("Patient"));
|
||||||
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
|
||||||
|
DaoMethodOutcome daoMethodOutcome;
|
||||||
|
|
||||||
sp.setExpression("Patient.telecom.where(system='phone' or system='email') | Patient.telecome.where(system='email')");
|
sp.setExpression("Patient.telecom.where(system='phone' or system='email') | Patient.telecome.where(system='email')");
|
||||||
sp.setCode("telephone-unformatted-3");
|
sp.setCode("telephone-unformatted-3");
|
||||||
daoMethodOutcome = mySearchParameterDao.create(sp);
|
daoMethodOutcome = mySearchParameterDao.create(sp);
|
||||||
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateSpWithMultiplePaths4() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setCode("telephone-unformatted");
|
||||||
|
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
sp.setExpression("Patient.telecom.where(system='phone' or system='email')");
|
||||||
|
sp.getBase().add(new CodeType("Patient"));
|
||||||
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
|
||||||
|
DaoMethodOutcome daoMethodOutcome;
|
||||||
|
|
||||||
sp.setExpression("Patient.telecom.where(system='phone' or system='email') | Patient.telecom.where(system='email') or Patient.telecom.where(system='mail' | system='phone')");
|
sp.setExpression("Patient.telecom.where(system='phone' or system='email') | Patient.telecom.where(system='email') or Patient.telecom.where(system='mail' | system='phone')");
|
||||||
sp.setCode("telephone-unformatted-3");
|
sp.setCode("telephone-unformatted-3");
|
||||||
|
@ -120,7 +158,6 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateInvalidParamNoPath() {
|
public void testCreateInvalidParamNoPath() {
|
||||||
SearchParameter fooSp = new SearchParameter();
|
SearchParameter fooSp = new SearchParameter();
|
||||||
|
@ -1110,21 +1147,21 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
medAdmin.setMedication(new Reference(medication));
|
medAdmin.setMedication(new Reference(medication));
|
||||||
|
|
||||||
myMedicationAdministrationDao.create(medAdmin);
|
myMedicationAdministrationDao.create(medAdmin);
|
||||||
|
|
||||||
runInTransaction(()->{
|
runInTransaction(() -> {
|
||||||
List<ResourceIndexedSearchParamToken> tokens = myResourceIndexedSearchParamTokenDao
|
List<ResourceIndexedSearchParamToken> tokens = myResourceIndexedSearchParamTokenDao
|
||||||
.findAll()
|
.findAll()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(t -> t.getParamName().equals("medicationadministration-ingredient-medication"))
|
.filter(t -> t.getParamName().equals("medicationadministration-ingredient-medication"))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
ourLog.info("Tokens:\n * {}", tokens.stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
|
ourLog.info("Tokens:\n * {}", tokens.stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
||||||
assertEquals(1, tokens.size(), tokens.toString());
|
assertEquals(1, tokens.size(), tokens.toString());
|
||||||
assertEquals(false, tokens.get(0).isMissing());
|
assertEquals(false, tokens.get(0).isMissing());
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||||
map.add("medicationadministration-ingredient-medication", new TokenParam("system","code"));
|
map.add("medicationadministration-ingredient-medication", new TokenParam("system", "code"));
|
||||||
myCaptureQueriesListener.clear();
|
myCaptureQueriesListener.clear();
|
||||||
IBundleProvider search = myMedicationAdministrationDao.search(map);
|
IBundleProvider search = myMedicationAdministrationDao.search(map);
|
||||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||||
|
@ -1133,5 +1170,4 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,14 +49,16 @@ public class FhirResourceDaoDstu3UpdateTest extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
CodeSystem codeSystem = new CodeSystem();
|
CodeSystem codeSystem = new CodeSystem();
|
||||||
codeSystem.setUrl("http://foo");
|
codeSystem.setUrl("http://foo");
|
||||||
IIdType id = myCodeSystemDao.create(codeSystem).getId().toUnqualifiedVersionless();
|
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
myCodeSystemDao.delete(id);
|
myCodeSystemDao.delete(id, mySrd);
|
||||||
|
|
||||||
codeSystem = new CodeSystem();
|
codeSystem = new CodeSystem();
|
||||||
codeSystem.setUrl("http://foo");
|
codeSystem.setUrl("http://foo");
|
||||||
myCodeSystemDao.update(codeSystem, "Patient?name=FAM").getId().toUnqualifiedVersionless();
|
IIdType id2 = myCodeSystemDao.update(codeSystem, "CodeSystem?url=http://foo", mySrd).getId();
|
||||||
|
|
||||||
|
assertNotEquals(id.getIdPart(), id2.getIdPart());
|
||||||
|
assertEquals("1", id2.getVersionIdPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1117,7 +1117,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
String encoded = myFhirContext.newXmlParser().encodeResourceToString(response.getOperationOutcome());
|
String encoded = myFhirContext.newXmlParser().encodeResourceToString(response.getOperationOutcome());
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
assertThat(encoded, containsString(
|
assertThat(encoded, containsString(
|
||||||
"<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted 2 resource(s) in "));
|
"<diagnostics value=\"Successfully deleted 2 resource(s)"));
|
||||||
try {
|
try {
|
||||||
myClient.read().resource("Patient").withId(id1).execute();
|
myClient.read().resource("Patient").withId(id1).execute();
|
||||||
fail();
|
fail();
|
||||||
|
@ -1144,7 +1144,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
ourLog.info(response);
|
ourLog.info(response);
|
||||||
assertEquals(200, resp.getStatusLine().getStatusCode());
|
assertEquals(200, resp.getStatusLine().getStatusCode());
|
||||||
assertThat(response, containsString(
|
assertThat(response, containsString(
|
||||||
"<issue><severity value=\"warning\"/><code value=\"not-found\"/><diagnostics value=\"Unable to find resource matching URL "Patient?identifier=testDeleteConditionalNoMatches". Deletion failed.\"/></issue>"));
|
"<diagnostics value=\"Unable to find resource matching URL "Patient?identifier=testDeleteConditionalNoMatches". Nothing has been deleted.\"/>"));
|
||||||
} finally {
|
} finally {
|
||||||
IOUtils.closeQuietly(resp);
|
IOUtils.closeQuietly(resp);
|
||||||
}
|
}
|
||||||
|
@ -1214,7 +1214,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
OperationOutcome oo = myFhirContext.newXmlParser().parseResource(OperationOutcome.class, resp);
|
OperationOutcome oo = myFhirContext.newXmlParser().parseResource(OperationOutcome.class, resp);
|
||||||
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in "));
|
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s). Took "));
|
||||||
} finally {
|
} finally {
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
|
@ -1241,7 +1241,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
OperationOutcome oo = myFhirContext.newXmlParser().parseResource(OperationOutcome.class, resp);
|
OperationOutcome oo = myFhirContext.newXmlParser().parseResource(OperationOutcome.class, resp);
|
||||||
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Unable to find resource matching URL \"Patient?name=testDeleteResourceConditional1\". Deletion failed."));
|
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Unable to find resource matching URL \"Patient?name=testDeleteResourceConditional1\". Nothing has been deleted."));
|
||||||
} finally {
|
} finally {
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
|
@ -1322,7 +1322,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
MethodOutcome resp = myClient.delete().resourceById(id).execute();
|
MethodOutcome resp = myClient.delete().resourceById(id).execute();
|
||||||
OperationOutcome oo = (OperationOutcome) resp.getOperationOutcome();
|
OperationOutcome oo = (OperationOutcome) resp.getOperationOutcome();
|
||||||
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in "));
|
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s). Took"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -192,14 +192,11 @@ public class SystemProviderTransactionSearchDstu3Test extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPatchUsingJsonPatch_Transaction() throws Exception {
|
public void testPatchUsingJsonPatch_Transaction() {
|
||||||
String methodName = "testPatchUsingJsonPatch_Transaction";
|
|
||||||
IIdType pid1;
|
IIdType pid1;
|
||||||
{
|
{
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.setActive(true);
|
patient.setActive(true);
|
||||||
patient.addIdentifier().setSystem("urn:system").setValue("0");
|
|
||||||
patient.addName().setFamily(methodName).addGiven("Joe");
|
|
||||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,6 +221,7 @@ public class SystemProviderTransactionSearchDstu3Test extends BaseJpaDstu3Test {
|
||||||
.getRequest().setUrl(pid1.getValue()).setMethod(HTTPVerb.PUT);
|
.getRequest().setUrl(pid1.getValue()).setMethod(HTTPVerb.PUT);
|
||||||
|
|
||||||
Bundle bundle = ourClient.transaction().withBundle(input).execute();
|
Bundle bundle = ourClient.transaction().withBundle(input).execute();
|
||||||
|
ourLog.info("Response: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||||
|
|
||||||
//Validate over all bundle response entry contents.
|
//Validate over all bundle response entry contents.
|
||||||
assertThat(bundle.getType(), is(equalTo(Bundle.BundleType.TRANSACTIONRESPONSE)));
|
assertThat(bundle.getType(), is(equalTo(Bundle.BundleType.TRANSACTIONRESPONSE)));
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
<version>6.3.2-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -404,18 +404,6 @@ public class Batch2CoordinatorIT extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStepRunFailure_continuouslyThrows_marksJobFailed() {
|
public void testStepRunFailure_continuouslyThrows_marksJobFailed() {
|
||||||
// FIXME: remove
|
|
||||||
// AtomicInteger interceptorCounter = new AtomicInteger();
|
|
||||||
// myWorkChannel.addInterceptor(new ExecutorChannelInterceptor() {
|
|
||||||
// @Override
|
|
||||||
// public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) {
|
|
||||||
// if (ex != null) {
|
|
||||||
// interceptorCounter.incrementAndGet();
|
|
||||||
// ourLog.info("Work Channel Exception thrown: {}. Resending message", ex.getMessage());
|
|
||||||
// channel.send(message);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
AtomicInteger counter = new AtomicInteger();
|
AtomicInteger counter = new AtomicInteger();
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue