merge master
This commit is contained in:
commit
d70be51003
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
||||
<version>6.3.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -22,10 +22,13 @@ package ca.uhn.fhir.context.support;
|
|||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
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.parser.LenientErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.BundleUtil;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
|
@ -321,6 +324,19 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
} else {
|
||||
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) {
|
||||
|
|
|
@ -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 org.apache.commons.lang3.StringUtils;
|
||||
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 java.io.Serializable;
|
||||
|
@ -64,6 +65,23 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
*/
|
||||
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
|
||||
* 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>
|
||||
* </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;
|
||||
|
||||
@Override
|
||||
public InstantDt get(IResource theResource) {
|
||||
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public IPrimitiveType<Date> get(IAnyResource theResource) {
|
||||
return (IPrimitiveType<Date>) theResource.getUserData(DELETED_AT.name());
|
||||
public IPrimitiveType<Date> get(IResource theResource) {
|
||||
return (IPrimitiveType<Date>) theResource.getResourceMetadata().get(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(IResource theResource, InstantDt theObject) {
|
||||
theResource.getResourceMetadata().put(DELETED_AT, theObject);
|
||||
public void put(IResource theResource, IPrimitiveType<Date> 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.IBaseBackboneElement;
|
||||
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.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
|
@ -40,7 +43,7 @@ import java.util.Objects;
|
|||
* (method and search).
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* <p>
|
||||
* This is not yet complete, and doesn't support all FHIR features. <b>USE WITH CAUTION</b> as the API
|
||||
* may change.
|
||||
*
|
||||
|
@ -101,10 +104,8 @@ public class BundleBuilder {
|
|||
/**
|
||||
* Sets the specified primitive field on the bundle with the value provided.
|
||||
*
|
||||
* @param theFieldName
|
||||
* Name of the primitive field.
|
||||
* @param theFieldValue
|
||||
* Value of the field to be set.
|
||||
* @param theFieldName Name of the primitive field.
|
||||
* @param theFieldValue Value of the field to be set.
|
||||
*/
|
||||
public BundleBuilder setBundleField(String theFieldName, String theFieldValue) {
|
||||
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.
|
||||
*
|
||||
* @param theSearch
|
||||
* Search part of the entry
|
||||
* @param theFieldName
|
||||
* Name of the primitive field.
|
||||
* @param theFieldValue
|
||||
* Value of the field to be set.
|
||||
* @param theSearch Search part of the entry
|
||||
* @param theFieldName Name of the primitive field.
|
||||
* @param theFieldValue Value of the field to be set.
|
||||
*/
|
||||
public BundleBuilder setSearchField(IBase theSearch, String theFieldName, String theFieldValue) {
|
||||
BaseRuntimeChildDefinition typeChild = mySearchDef.getChildByName(theFieldName);
|
||||
|
@ -144,6 +142,37 @@ public class BundleBuilder {
|
|||
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.
|
||||
* 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
|
||||
*/
|
||||
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");
|
||||
|
||||
IBase request = addEntryAndReturnRequest(theResource);
|
||||
IBase request = addEntryAndReturnRequest(theResource, theFullUrl);
|
||||
|
||||
// Bundle.entry.request.url
|
||||
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||
String resourceType = myContext.getResourceType(theResource);
|
||||
url.setValueAsString(theResource.getIdElement().toUnqualifiedVersionless().withResourceType(resourceType).getValue());
|
||||
url.setValueAsString(theRequestUrl);
|
||||
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
||||
|
||||
// Bundle.entry.request.url
|
||||
// Bundle.entry.request.method
|
||||
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
||||
method.setValueAsString("PUT");
|
||||
method.setValueAsString(theHttpVerb);
|
||||
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
||||
|
||||
return new UpdateBuilder(url);
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,7 +224,7 @@ public class BundleBuilder {
|
|||
public CreateBuilder addTransactionCreateEntry(IBaseResource theResource) {
|
||||
setBundleField("type", "transaction");
|
||||
|
||||
IBase request = addEntryAndReturnRequest(theResource);
|
||||
IBase request = addEntryAndReturnRequest(theResource, theResource.getIdElement().getValue());
|
||||
|
||||
String resourceType = myContext.getResourceType(theResource);
|
||||
|
||||
|
@ -198,15 +244,30 @@ public class BundleBuilder {
|
|||
/**
|
||||
* 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 theResource The resource to delete.
|
||||
*/
|
||||
public void addTransactionDeleteEntry(IBaseResource theResource) {
|
||||
public DeleteBuilder addTransactionDeleteEntry(IBaseResource theResource) {
|
||||
String resourceType = myContext.getResourceType(theResource);
|
||||
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.
|
||||
*
|
||||
* @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");
|
||||
IBase request = addEntryAndReturnRequest();
|
||||
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
|
||||
IPrimitiveType<?> url = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||
url.setValueAsString(idDt.toUnqualifiedVersionless().withResourceType(theResourceType).getValue());
|
||||
url.setValueAsString(theDeleteUrl);
|
||||
myEntryRequestUrlChild.getMutator().setValue(request, url);
|
||||
|
||||
// Bundle.entry.request.method
|
||||
IPrimitiveType<?> method = (IPrimitiveType<?>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
|
||||
method.setValueAsString("DELETE");
|
||||
myEntryRequestMethodChild.getMutator().setValue(request, method);
|
||||
}
|
||||
|
||||
return new DeleteBuilder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -239,14 +321,13 @@ public class BundleBuilder {
|
|||
*/
|
||||
public void addCollectionEntry(IBaseResource theResource) {
|
||||
setType("collection");
|
||||
addEntryAndReturnRequest(theResource);
|
||||
addEntryAndReturnRequest(theResource, theResource.getIdElement().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new entry and adds it to the bundle
|
||||
*
|
||||
* @return
|
||||
* Returns the new entry.
|
||||
* @return Returns the new entry.
|
||||
*/
|
||||
public IBase addEntry() {
|
||||
IBase entry = myEntryDef.newInstance();
|
||||
|
@ -258,8 +339,7 @@ public class BundleBuilder {
|
|||
* Creates new search instance for the specified entry
|
||||
*
|
||||
* @param entry Entry to create search instance for
|
||||
* @return
|
||||
* Returns the search instance
|
||||
* @return Returns the search instance
|
||||
*/
|
||||
public IBaseBackboneElement addSearch(IBase entry) {
|
||||
IBase searchInstance = mySearchDef.newInstance();
|
||||
|
@ -267,19 +347,14 @@ public class BundleBuilder {
|
|||
return (IBaseBackboneElement) searchInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param theResource
|
||||
* @return
|
||||
*/
|
||||
public IBase addEntryAndReturnRequest(IBaseResource theResource) {
|
||||
private IBase addEntryAndReturnRequest(IBaseResource theResource, String theFullUrl) {
|
||||
Validate.notNull(theResource, "theResource must not be null");
|
||||
|
||||
IBase entry = addEntry();
|
||||
|
||||
// Bundle.entry.fullUrl
|
||||
IPrimitiveType<?> fullUrl = (IPrimitiveType<?>) myContext.getElementDefinition("uri").newInstance();
|
||||
fullUrl.setValueAsString(theResource.getIdElement().getValue());
|
||||
fullUrl.setValueAsString(theFullUrl);
|
||||
myEntryFullUrlChild.getMutator().setValue(entry, fullUrl);
|
||||
|
||||
// Bundle.entry.resource
|
||||
|
@ -306,6 +381,15 @@ public class BundleBuilder {
|
|||
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) {
|
||||
BaseRuntimeChildDefinition.IMutator mutator = myMetaDef.getChildByName(theFieldName).getMutator();
|
||||
mutator.setValue(myBundle.getMeta(), theFieldValue);
|
||||
|
@ -315,12 +399,9 @@ public class BundleBuilder {
|
|||
/**
|
||||
* Sets the specified entry field.
|
||||
*
|
||||
* @param theEntry
|
||||
* The entry instance to set values on
|
||||
* @param theEntryChildName
|
||||
* The child field name of the entry instance to be set
|
||||
* @param theValue
|
||||
* The field value to set
|
||||
* @param theEntry The entry instance to set values on
|
||||
* @param theEntryChildName 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) {
|
||||
addToBase(theEntry, theEntryChildName, theValue, myEntryDef);
|
||||
|
@ -329,12 +410,9 @@ public class BundleBuilder {
|
|||
/**
|
||||
* Sets the specified search field.
|
||||
*
|
||||
* @param theSearch
|
||||
* The search instance to set values on
|
||||
* @param theSearchFieldName
|
||||
* The child field name of the search instance to be set
|
||||
* @param theSearchFieldValue
|
||||
* The field value to set
|
||||
* @param theSearch The search instance to set values on
|
||||
* @param theSearchFieldName 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) {
|
||||
addToBase(theSearch, theSearchFieldName, theSearchFieldValue, mySearchDef);
|
||||
|
@ -349,12 +427,9 @@ public class BundleBuilder {
|
|||
/**
|
||||
* Creates a new primitive.
|
||||
*
|
||||
* @param theTypeName
|
||||
* The element type for the primitive
|
||||
* @param <T>
|
||||
* Actual type of the parameterized primitive type interface
|
||||
* @return
|
||||
* Returns the new empty instance of the element definition.
|
||||
* @param theTypeName The element type for the primitive
|
||||
* @param <T> 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) {
|
||||
BaseRuntimeElementDefinition primitiveDefinition = myContext.getElementDefinition(theTypeName);
|
||||
|
@ -365,14 +440,10 @@ public class BundleBuilder {
|
|||
/**
|
||||
* Creates a new primitive instance of the specified element type.
|
||||
*
|
||||
* @param theTypeName
|
||||
* Element type to create
|
||||
* @param theInitialValue
|
||||
* Initial value to be set on the new instance
|
||||
* @param <T>
|
||||
* Actual type of the parameterized primitive type interface
|
||||
* @return
|
||||
* Returns the newly created instance
|
||||
* @param theTypeName Element type to create
|
||||
* @param theInitialValue Initial value to be set on the new 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) {
|
||||
IPrimitiveType<T> retVal = newPrimitive(theTypeName);
|
||||
|
@ -389,38 +460,84 @@ public class BundleBuilder {
|
|||
setBundleField("type", theType);
|
||||
}
|
||||
|
||||
public static class UpdateBuilder {
|
||||
|
||||
private final IPrimitiveType<?> myUrl;
|
||||
public class DeleteBuilder extends BaseOperationBuilder {
|
||||
|
||||
public UpdateBuilder(IPrimitiveType<?> theUrl) {
|
||||
myUrl = theUrl;
|
||||
}
|
||||
// nothing yet
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
public CreateBuilder(IBase theRequest) {
|
||||
CreateBuilder(IBase theRequest) {
|
||||
myRequest = theRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this create a Conditional Create
|
||||
*/
|
||||
public void conditional(String theConditionalUrl) {
|
||||
public CreateBuilder conditional(String theConditionalUrl) {
|
||||
BaseRuntimeElementDefinition<?> stringDefinition = Objects.requireNonNull(myContext.getElementDefinition("string"));
|
||||
IPrimitiveType<?> ifNoneExist = (IPrimitiveType<?>) stringDefinition.newInstance();
|
||||
ifNoneExist.setValueAsString(theConditionalUrl);
|
||||
|
||||
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 java.io.IOException;
|
||||
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.zip.GZIPInputStream;
|
||||
|
||||
|
@ -86,6 +89,10 @@ public class ClasspathUtil {
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -29,11 +29,13 @@ import ca.uhn.fhir.i18n.Msg;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
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.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -53,8 +55,12 @@ public class OperationOutcomeUtil {
|
|||
* @return Returns the newly added issue
|
||||
*/
|
||||
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);
|
||||
populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode);
|
||||
populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode, theDetailSystem, theDetailCode, theDetailDescription);
|
||||
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());
|
||||
BaseRuntimeChildDefinition detailsChild;
|
||||
detailsChild = issueElement.getChildByName("diagnostics");
|
||||
BaseRuntimeChildDefinition diagnosticsChild;
|
||||
diagnosticsChild = issueElement.getChildByName("diagnostics");
|
||||
|
||||
BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code");
|
||||
IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments());
|
||||
codeElem.setValueAsString(theCode);
|
||||
codeChild.getMutator().addValue(theIssue, codeElem);
|
||||
|
||||
BaseRuntimeElementDefinition<?> stringDef = detailsChild.getChildByName(detailsChild.getElementName());
|
||||
BaseRuntimeElementDefinition<?> stringDef = diagnosticsChild.getChildByName(diagnosticsChild.getElementName());
|
||||
BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
|
||||
|
||||
IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments());
|
||||
|
@ -146,9 +152,27 @@ public class OperationOutcomeUtil {
|
|||
|
||||
IPrimitiveType<?> string = (IPrimitiveType<?>) stringDef.newInstance();
|
||||
string.setValueAsString(theDetails);
|
||||
detailsChild.getMutator().setValue(theIssue, string);
|
||||
diagnosticsChild.getMutator().setValue(theIssue, string);
|
||||
|
||||
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) {
|
||||
|
|
|
@ -107,6 +107,7 @@ public enum VersionEnum {
|
|||
V6_1_4,
|
||||
V6_2_0,
|
||||
V6_2_1,
|
||||
// Dev Build
|
||||
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.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.unableToDeleteNotFound=Unable to find resource matching URL "{0}". Deletion failed.
|
||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreate=Successfully created resource "{0}" in {1}ms
|
||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulUpdate=Successfully updated resource "{0}" in {1}ms
|
||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulDeletes=Successfully deleted {0} resource(s) in {1}ms
|
||||
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}".
|
||||
ca.uhn.fhir.jpa.dao.BaseStorageDao.successfulCreateConditionalNoMatch=Successfully conditionally created resource "{0}". No existing resources matched URL "{1}".
|
||||
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.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}
|
||||
|
@ -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.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.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
|
||||
*/
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
import static org.hamcrest.CoreMatchers.hasItem;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class VersionEnumTest {
|
||||
|
||||
|
@ -18,10 +19,25 @@ public class VersionEnumTest {
|
|||
.collect(Collectors.toList());
|
||||
|
||||
String version = VersionUtil.getVersion();
|
||||
version = "V" + version.replace(".", "_");
|
||||
|
||||
version = version.replaceAll("-PRE[0-9]+", "");
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
||||
<version>6.3.2-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
||||
<version>6.3.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
||||
<version>6.3.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
||||
<version>6.3.2-SNAPSHOT</version>
|
||||
<relativePath>../../hapi-deployable-pom</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
||||
<version>6.3.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</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.ValueSet;
|
||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||
import org.hl7.fhir.r5.model.SearchParameter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
/**
|
||||
* This class converts versions of various resources to/from a canonical version
|
||||
* 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_40_50 ADVISOR_40_50 = new BaseAdvisor_40_50(false);
|
||||
private static final BaseAdvisor_43_50 ADVISOR_43_50 = new BaseAdvisor_43_50(false);
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final IStrategy myStrategy;
|
||||
|
||||
public VersionCanonicalizer(FhirContext theTargetContext) {
|
||||
this(theTargetContext.getVersion().getVersion());
|
||||
}
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
@SuppressWarnings({"EnumSwitchStatementWhichMissesCases", "EnhancedSwitchMigration"})
|
||||
public VersionCanonicalizer(FhirVersionEnum theTargetVersion) {
|
||||
switch (theTargetVersion) {
|
||||
case DSTU2:
|
||||
|
@ -160,9 +162,13 @@ public class VersionCanonicalizer {
|
|||
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);
|
||||
|
||||
|
@ -175,9 +181,11 @@ public class VersionCanonicalizer {
|
|||
IBaseResource valueSetFromCanonical(ValueSet theValueSet);
|
||||
|
||||
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();
|
||||
|
||||
|
@ -189,7 +197,7 @@ public class VersionCanonicalizer {
|
|||
}
|
||||
|
||||
@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);
|
||||
return (CapabilityStatement) VersionConvertorFactory_10_50.convertResource(reencoded, ADVISOR_10_50);
|
||||
}
|
||||
|
@ -224,8 +232,7 @@ public class VersionCanonicalizer {
|
|||
@Override
|
||||
public ValueSet valueSetToCanonical(IBaseResource theValueSet) {
|
||||
org.hl7.fhir.dstu2.model.Resource reencoded = reencodeToHl7Org(theValueSet);
|
||||
ValueSet valueSet = (ValueSet) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
|
||||
return valueSet;
|
||||
return (ValueSet) VersionConvertorFactory_10_40.convertResource(reencoded, ADVISOR_10_40);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -275,6 +282,16 @@ public class VersionCanonicalizer {
|
|||
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) {
|
||||
if (myHl7OrgStructures) {
|
||||
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
|
||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.dstu3.model.Resource theCapabilityStatement) {
|
||||
return (CapabilityStatement) VersionConvertorFactory_30_50.convertResource(theCapabilityStatement, ADVISOR_30_50);
|
||||
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||
return (CapabilityStatement) VersionConvertorFactory_30_50.convertResource((org.hl7.fhir.dstu3.model.Resource) theCapabilityStatement, ADVISOR_30_50);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -327,12 +344,17 @@ public class VersionCanonicalizer {
|
|||
public ConceptMap conceptMapToCanonical(IBaseResource theConceptMap) {
|
||||
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
|
||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r4.model.Resource theCapabilityStatement) {
|
||||
return (CapabilityStatement) VersionConvertorFactory_40_50.convertResource(theCapabilityStatement, ADVISOR_40_50);
|
||||
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||
return (CapabilityStatement) VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r4.model.Resource) theCapabilityStatement, ADVISOR_40_50);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -365,13 +387,18 @@ public class VersionCanonicalizer {
|
|||
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
|
||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r4b.model.Resource theCapabilityStatement) {
|
||||
return (CapabilityStatement) VersionConvertorFactory_43_50.convertResource(theCapabilityStatement, ADVISOR_43_50);
|
||||
public CapabilityStatement capabilityStatementToCanonical(IBaseResource theCapabilityStatement) {
|
||||
return (CapabilityStatement) VersionConvertorFactory_43_50.convertResource((org.hl7.fhir.r4b.model.Resource) theCapabilityStatement, ADVISOR_43_50);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -410,13 +437,18 @@ public class VersionCanonicalizer {
|
|||
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
|
||||
public CapabilityStatement capabilityStatementToCanonical(org.hl7.fhir.r5.model.Resource theCapabilityStatement) {
|
||||
public CapabilityStatement capabilityStatementToCanonical(IBaseResource 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchParameter searchParameterToCanonical(IBaseResource theSearchParameter) {
|
||||
return (SearchParameter) theSearchParameter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.3.0-PRE5-SNAPSHOT</version>
|
||||
<version>6.3.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -26,6 +26,11 @@ import ca.uhn.fhir.util.BundleBuilder;
|
|||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
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 java.math.BigDecimal;
|
||||
|
@ -109,6 +114,53 @@ public class BundleBuilderExamples {
|
|||
//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 {
|
||||
//START SNIPPET: customizeBundle
|
||||
// Create a TransactionBuilder
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
---
|
||||
release-date: "FILL ME"
|
||||
release-date: "2022-07-08"
|
||||
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>
|
||||
<li>SLF4j (All): 1.7.33 -> 2.0.3</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>Jetty (CLI): 9.4.48.v20220622 -> 10.0.12</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.
|
||||
|
||||
<a href="/hapi-fhir/docs/images/interceptors-server-pipeline.svg" target="_blank">Expand</a>
|
||||
<img src="/hapi-fhir/docs/images/interceptors-server-pipeline.svg" alt="Server Pipeline"/>
|
||||
<a href="server_pointcuts/interceptors-server-pipeline.svg" target="_blank">Expand</a>
|
||||
<img src="server_pointcuts/interceptors-server-pipeline.svg" alt="Server Pipeline"/>
|
||||
|
||||
# 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.
|
||||
|
||||
<a href="/hapi-fhir/docs/images/interceptors-server-jpa-pipeline.svg" target="_blank">Expand</a>
|
||||
<img src="/hapi-fhir/docs/images/interceptors-server-jpa-pipeline.svg" alt="Server Pipeline"/>
|
||||
<a href="server_pointcuts/interceptors-server-jpa-pipeline.svg" target="_blank">Expand</a>
|
||||
<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}}
|
||||
```
|
||||
|
||||
# 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:
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
|
|
@ -39,14 +39,16 @@ public class SqlQuery {
|
|||
private final StackTraceElement[] myStackTrace;
|
||||
private final int mySize;
|
||||
private final LanguageEnum myLanguage;
|
||||
private final String myNamespace;
|
||||
|
||||
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");
|
||||
|
||||
myNamespace = theNamespace;
|
||||
mySql = theSql;
|
||||
myParams = Collections.unmodifiableList(theParams);
|
||||
myQueryTimestamp = theQueryTimestamp;
|
||||
|
@ -56,6 +58,10 @@ public class SqlQuery {
|
|||
myLanguage = theLanguage;
|
||||
}
|
||||
|
||||
public String getNamespace() {
|
||||
return myNamespace;
|
||||
}
|
||||
|
||||
public long getQueryTimestamp() {
|
||||
return myQueryTimestamp;
|
||||
}
|
||||
|
@ -118,6 +124,10 @@ public class SqlQuery {
|
|||
return mySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getSql(true, true);
|
||||
}
|
||||
|
||||
public enum LanguageEnum {
|
||||
|
||||
|
@ -125,9 +135,4 @@ public class SqlQuery {
|
|||
JSON
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getSql(true, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -384,7 +384,7 @@ public class TestUtil {
|
|||
}
|
||||
|
||||
public static void sleepOneClick() {
|
||||
ca.uhn.fhir.util.TestUtil.sleepAtLeast(1);
|
||||
ca.uhn.fhir.util.TestUtil.sleepAtLeast(1, false);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public String storeWorkChunk(BatchWorkChunk theBatchWorkChunk) {
|
||||
Batch2WorkChunkEntity entity = new Batch2WorkChunkEntity();
|
||||
entity.setId(UUID.randomUUID().toString());
|
||||
|
@ -102,7 +102,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public Optional<WorkChunk> fetchWorkChunkSetStartTimeAndMarkInProgress(String theChunkId) {
|
||||
myWorkChunkRepository.updateChunkStatusForStart(theChunkId, new Date(), StatusEnum.IN_PROGRESS);
|
||||
Optional<Batch2WorkChunkEntity> chunk = myWorkChunkRepository.findById(theChunkId);
|
||||
|
@ -110,7 +110,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public String storeNewInstance(JobInstance theInstance) {
|
||||
Validate.isTrue(isBlank(theInstance.getInstanceId()));
|
||||
|
||||
|
@ -271,7 +271,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public boolean canAdvanceInstanceToNextStep(String theInstanceId, String 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);
|
||||
|
|
|
@ -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.DeleteExpungeSvcImpl;
|
||||
import ca.uhn.fhir.jpa.reindex.Batch2DaoSvcImpl;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
@ -43,7 +44,7 @@ public class Batch2SupportConfig {
|
|||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,11 @@ import ca.uhn.fhir.jpa.dao.DaoSearchParamProvider;
|
|||
import ca.uhn.fhir.jpa.dao.HistoryBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.HistoryBuilderFactory;
|
||||
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.JpaStorageResourceParser;
|
||||
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.TransactionProcessor;
|
||||
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.sp.ISearchParamPresenceSvc;
|
||||
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.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.ITermReadSvc;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReindexingSvc;
|
||||
import ca.uhn.fhir.jpa.term.config.TermCodeSystemConfig;
|
||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||
import ca.uhn.fhir.jpa.validation.ResourceLoaderImpl;
|
||||
|
@ -225,7 +232,6 @@ public class JpaConfig {
|
|||
return new ResponseTerminologyTranslationInterceptor(theValidationSupport, theResponseTerminologyTranslationSvc);
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public ResponseTerminologyTranslationSvc responseTerminologyTranslationSvc(IValidationSupport theValidationSupport) {
|
||||
return new ResponseTerminologyTranslationSvc(theValidationSupport);
|
||||
|
@ -260,6 +266,11 @@ public class JpaConfig {
|
|||
return new ValueSetOperationProvider();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IJpaStorageResourceParser jpaStorageResourceParser() {
|
||||
return new JpaStorageResourceParser();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransactionProcessor transactionProcessor() {
|
||||
return new TransactionProcessor();
|
||||
|
@ -763,4 +774,22 @@ public class JpaConfig {
|
|||
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.config.GeneratedDaoAndResourceProviderConfigDstu3;
|
||||
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.dstu3.TransactionProcessorVersionAdapterDstu3;
|
||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||
|
@ -53,7 +52,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||
@Import({
|
||||
FhirContextDstu3Config.class,
|
||||
GeneratedDaoAndResourceProviderConfigDstu3.class,
|
||||
SharedConfigDstu3Plus.class,
|
||||
JpaConfig.class
|
||||
})
|
||||
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.config.GeneratedDaoAndResourceProviderConfigR4;
|
||||
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.r4.TransactionProcessorVersionAdapterR4;
|
||||
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.ITermLoaderSvc;
|
||||
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 org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Consent;
|
||||
|
@ -62,7 +60,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||
@Import({
|
||||
FhirContextR4Config.class,
|
||||
GeneratedDaoAndResourceProviderConfigR4.class,
|
||||
SharedConfigDstu3Plus.class,
|
||||
JpaConfig.class
|
||||
})
|
||||
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.config.GeneratedDaoAndResourceProviderConfigR4B;
|
||||
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.r4b.TransactionProcessorVersionAdapterR4B;
|
||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||
|
@ -53,7 +52,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||
@Import({
|
||||
FhirContextR4BConfig.class,
|
||||
GeneratedDaoAndResourceProviderConfigR4B.class,
|
||||
SharedConfigDstu3Plus.class,
|
||||
JpaConfig.class
|
||||
})
|
||||
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.config.GeneratedDaoAndResourceProviderConfigR5;
|
||||
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.r5.TransactionProcessorVersionAdapterR5;
|
||||
import ca.uhn.fhir.jpa.graphql.GraphQLProvider;
|
||||
|
@ -53,7 +52,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||
@Import({
|
||||
FhirContextR5Config.class,
|
||||
GeneratedDaoAndResourceProviderConfigR5.class,
|
||||
SharedConfigDstu3Plus.class,
|
||||
JpaConfig.class
|
||||
})
|
||||
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.IDao;
|
||||
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.ISearchCoordinatorSvc;
|
||||
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.entity.PartitionEntity;
|
||||
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.cross.IBasePersistedResource;
|
||||
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.TagTypeEnum;
|
||||
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.util.JpaConstants;
|
||||
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.ResourceIndexedSearchParams;
|
||||
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.model.api.IResource;
|
||||
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.TagList;
|
||||
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.rest.api.Constants;
|
||||
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.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.rest.param.HistorySearchStyleEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
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.servlet.ServletRequestDetails;
|
||||
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.HapiExtensions;
|
||||
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 com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Charsets;
|
||||
|
@ -148,11 +146,9 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -181,9 +177,18 @@ import static org.apache.commons.lang3.StringUtils.trim;
|
|||
* #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")
|
||||
@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_INDEXING_FAILED = 2L;
|
||||
|
@ -233,8 +238,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
@Autowired
|
||||
private PartitionSettings myPartitionSettings;
|
||||
@Autowired
|
||||
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||
@Autowired
|
||||
private IPartitionLookupSvc myPartitionLookupSvc;
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
|
@ -243,6 +246,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
@Autowired
|
||||
private PlatformTransactionManager myTransactionManager;
|
||||
@Autowired
|
||||
protected IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setSearchParamPresenceSvc(ISearchParamPresenceSvc theSearchParamPresenceSvc) {
|
||||
|
@ -371,14 +376,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
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
|
||||
*/
|
||||
|
@ -513,27 +510,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
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) {
|
||||
String newVersion;
|
||||
|
@ -796,133 +772,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
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
|
||||
*
|
||||
|
@ -954,6 +803,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
@CoverageIgnore
|
||||
public BaseHasResource readEntity(IIdType theValueId, RequestDetails theRequest) {
|
||||
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());
|
||||
}
|
||||
|
||||
@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) {
|
||||
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)) {
|
||||
ourLog.debug("Resource {} has not changed", entity.getIdDt().toUnqualified().getValue());
|
||||
if (theResource != null) {
|
||||
updateResourceMetadata(entity, theResource);
|
||||
myJpaStorageResourceParser.updateResourceMetadata(entity, theResource);
|
||||
}
|
||||
entity.setUnchangedInCurrentOperation(true);
|
||||
return entity;
|
||||
|
@ -1475,7 +1113,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
}
|
||||
|
||||
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()) {
|
||||
oldResource = null;
|
||||
} else {
|
||||
oldResource = toResource(entity, false);
|
||||
oldResource = myJpaStorageResourceParser.toResource(entity, false);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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
|
||||
addPidToResource(savedEntity, theResource);
|
||||
|
||||
|
@ -1537,7 +1175,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
if (!changed && myConfig.isSuppressUpdatesWithNoChange() && (historyEntity.getVersion() > 1)) {
|
||||
ourLog.debug("Resource {} has not changed", historyEntity.getIdDt().toUnqualified().getValue());
|
||||
updateResourceMetadata(historyEntity, theResource);
|
||||
myJpaStorageResourceParser.updateResourceMetadata(historyEntity, theResource);
|
||||
return historyEntity;
|
||||
}
|
||||
|
||||
|
@ -1556,7 +1194,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
historyEntity.setResourceTextVc(encodedResource.getResourceText());
|
||||
myResourceHistoryTableDao.save(historyEntity);
|
||||
|
||||
updateResourceMetadata(historyEntity, theResource);
|
||||
myJpaStorageResourceParser.updateResourceMetadata(historyEntity, theResource);
|
||||
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* Throws an exception if the partition of the request, and the partition of the existing entity do not match.
|
||||
*
|
||||
* @param theRequest the request.
|
||||
* @param entity the existing entity.
|
||||
* @param entity the existing entity.
|
||||
*/
|
||||
private void failIfPartitionMismatch(RequestDetails theRequest, ResourceTable entity) {
|
||||
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 should never be null
|
||||
if (partitionEntity != null && !partitionEntity.getId().equals(entity.getPartitionId().getPartitionId())) {
|
||||
|
@ -1668,8 +1307,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
}
|
||||
|
||||
@Override
|
||||
public ResourceTable updateInternal(RequestDetails theRequestDetails, T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion,
|
||||
IBasePersistedResource theEntity, IIdType theResourceId, IBaseResource theOldResource, TransactionDetails theTransactionDetails) {
|
||||
public DaoMethodOutcome updateInternal(RequestDetails theRequestDetails, T theResource, String theMatchUrl, boolean thePerformIndexing, boolean theForceUpdateVersion,
|
||||
IBasePersistedResource theEntity, IIdType theResourceId, @Nullable IBaseResource theOldResource, RestOperationTypeEnum theOperationType, TransactionDetails theTransactionDetails) {
|
||||
|
||||
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
|
||||
updateResourceMetadata(savedEntity, theResource);
|
||||
myJpaStorageResourceParser.updateResourceMetadata(savedEntity, theResource);
|
||||
|
||||
// Populate the PID in the resource so it is available to hooks
|
||||
addPidToResource(savedEntity, theResource);
|
||||
|
@ -1706,7 +1345,42 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
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) {
|
||||
|
@ -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) {
|
||||
if (theElement == null) {
|
||||
|
@ -1896,6 +1551,22 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
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
|
||||
public static MemoryCacheService.TagDefinitionCacheKey toTagDefinitionMemoryCacheKey(TagTypeEnum theTagType, String theScheme, String theTerm) {
|
||||
return new MemoryCacheService.TagDefinitionCacheKey(theTagType, theScheme, theTerm);
|
||||
|
@ -1999,34 +1670,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
ourValidationDisabledForUnitTest = theValidationDisabledForUnitTest;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* Do not call this method outside of unit tests
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void setJpaStorageResourceParserForUnitTest(IJpaStorageResourceParser theJpaStorageResourceParser) {
|
||||
myJpaStorageResourceParser = theJpaStorageResourceParser;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.partition.IRequestPartitionHelperSvc;
|
||||
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.PersistedJpaBundleProviderFactory;
|
||||
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||
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.primitive.IdDt;
|
||||
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.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.InterceptorInvocationTimingEnum;
|
||||
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.SearchContainedModeEnum;
|
||||
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.util.CompositeInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.IInstanceValidatorModule;
|
||||
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.IBaseMetaType;
|
||||
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.IIdType;
|
||||
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";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class);
|
||||
|
||||
@Autowired
|
||||
protected PlatformTransactionManager myPlatformTransactionManager;
|
||||
@Autowired(required = false)
|
||||
|
@ -182,18 +177,37 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
private IDeleteExpungeJobSubmitter myDeleteExpungeJobSubmitter;
|
||||
@Autowired
|
||||
private IJobCoordinator myJobCoordinator;
|
||||
|
||||
private IInstanceValidatorModule myInstanceValidator;
|
||||
private String myResourceName;
|
||||
private Class<T> myResourceType;
|
||||
|
||||
@Autowired
|
||||
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
private TransactionTemplate myTxTemplate;
|
||||
|
||||
@Autowired
|
||||
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
|
||||
*/
|
||||
|
@ -220,11 +234,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
return create(theResource, theIfNoneExist, true, new TransactionDetails(), theRequestDetails);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTransactionService(HapiTransactionService theTransactionService) {
|
||||
myTransactionService = theTransactionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
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));
|
||||
|
@ -260,14 +269,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
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)}
|
||||
* 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();
|
||||
|
||||
preProcessResourceForStorage(theResource);
|
||||
|
@ -276,22 +285,21 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
ResourceTable entity = new ResourceTable();
|
||||
entity.setResourceType(toResourceName(theResource));
|
||||
entity.setPartitionId(myRequestPartitionHelperService.toStoragePartition(theRequestPartitionId));
|
||||
entity.setCreatedByMatchUrl(theIfNoneExist);
|
||||
entity.setCreatedByMatchUrl(theMatchUrl);
|
||||
entity.setVersion(1);
|
||||
|
||||
if (isNotBlank(theIfNoneExist)) {
|
||||
Set<JpaPid> match = myMatchResourceUrlService.processMatchUrl(theIfNoneExist, myResourceType, theTransactionDetails, theRequest)
|
||||
.stream().map(id -> (JpaPid) id).collect(Collectors.toSet());
|
||||
if (isNotBlank(theMatchUrl) && theProcessMatchUrl) {
|
||||
Set<ResourcePersistentId> match = myMatchResourceUrlService.processMatchUrl(theMatchUrl, myResourceType, theTransactionDetails, theRequest);
|
||||
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);
|
||||
} else if (match.size() == 1) {
|
||||
JpaPid pid = match.iterator().next();
|
||||
JpaPid pid = (JpaPid) match.iterator().next();
|
||||
|
||||
Supplier<LazyDaoMethodOutcome.EntityAndResource> entitySupplier = () -> {
|
||||
return myTxTemplate.execute(tx -> {
|
||||
ResourceTable foundEntity = myEntityManager.find(ResourceTable.class, pid.getId());
|
||||
IBaseResource resource = toResource(foundEntity, false);
|
||||
IBaseResource resource = myJpaStorageResourceParser.toResource(foundEntity, false);
|
||||
theResource.setId(resource.getIdElement().getValue());
|
||||
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);
|
||||
|
||||
// Pre-cache the match URL
|
||||
if (theIfNoneExist != null) {
|
||||
myMatchResourceUrlService.matchUrlResolved(theTransactionDetails, getResourceName(), theIfNoneExist, jpaPid);
|
||||
if (theMatchUrl != null) {
|
||||
myMatchResourceUrlService.matchUrlResolved(theTransactionDetails, getResourceName(), theMatchUrl, jpaPid);
|
||||
}
|
||||
|
||||
// Update the version/last updated in the resource so that interceptors get
|
||||
// the correct version
|
||||
// TODO - the above updateEntity calls updateResourceMetadata
|
||||
// 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
|
||||
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);
|
||||
}
|
||||
|
||||
DaoMethodOutcome outcome = toMethodOutcome(theRequest, entity, theResource).setCreated(true);
|
||||
DaoMethodOutcome outcome = toMethodOutcome(theRequest, entity, theResource, theMatchUrl, theOperationType)
|
||||
.setCreated(true);
|
||||
|
||||
if (!thePerformIndexing) {
|
||||
outcome.setId(theResource.getIdElement());
|
||||
}
|
||||
|
||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart());
|
||||
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
|
||||
populateOperationOutcomeForUpdate(w, outcome, theMatchUrl, theOperationType);
|
||||
|
||||
ourLog.debug(msg);
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
@ -533,8 +545,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
// if not found, return an outcome anyways.
|
||||
// Because no object actually existed, we'll
|
||||
// just set the id and nothing else
|
||||
DaoMethodOutcome outcome = createMethodOutcomeForResourceId(theId.getValue(), MESSAGE_KEY_DELETE_RESOURCE_NOT_EXISTING);
|
||||
return outcome;
|
||||
return createMethodOutcomeForResourceId(theId.getValue(), MESSAGE_KEY_DELETE_RESOURCE_NOT_EXISTING, StorageResponseCodeEnum.SUCCESSFUL_DELETE_NOT_FOUND);
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
outcome.setPersistentId(new JpaPid(entity.getResourceId()));
|
||||
|
@ -554,7 +565,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
T resourceToDelete = toResource(myResourceType, entity, null, false);
|
||||
T resourceToDelete = myJpaStorageResourceParser.toResource(myResourceType, entity, null, false);
|
||||
theDeleteConflicts.setResourceIdMarkedForDeletion(theId);
|
||||
|
||||
// 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);
|
||||
|
||||
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 message = getContext().getLocalizer().getMessage(BaseStorageDao.class, "successfulDeletes", 1, w.getMillis());
|
||||
String severity = "information";
|
||||
String code = "informational";
|
||||
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
|
||||
outcome.setOperationOutcome(oo);
|
||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulDeletes", 1);
|
||||
msg += " " + getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulTimingSuffix", w.getMillis());
|
||||
outcome.setOperationOutcome(createInfoOperationOutcome(msg, StorageResponseCodeEnum.SUCCESSFUL_DELETE));
|
||||
|
||||
return outcome;
|
||||
}
|
||||
|
@ -671,7 +679,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
ResourceTable entity = myEntityManager.find(ResourceTable.class, ((JpaPid) pid).getId());
|
||||
deletedResources.add(entity);
|
||||
|
||||
T resourceToDelete = toResource(myResourceType, entity, null, false);
|
||||
T resourceToDelete = myJpaStorageResourceParser.toResource(myResourceType, entity, null, false);
|
||||
|
||||
// Notify IServerOperationInterceptors about pre-action call
|
||||
HookParams hooks = new HookParams()
|
||||
|
@ -705,17 +713,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
IBaseOperationOutcome oo;
|
||||
if (deletedResources.isEmpty()) {
|
||||
oo = OperationOutcomeUtil.newInstance(getContext());
|
||||
String message = getMessageSanitized("unableToDeleteNotFound", theUrl);
|
||||
String severity = "warning";
|
||||
String code = "not-found";
|
||||
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
|
||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "unableToDeleteNotFound", theUrl);
|
||||
oo = createOperationOutcome(OO_SEVERITY_WARN, msg, "not-found", StorageResponseCodeEnum.SUCCESSFUL_DELETE_NOT_FOUND);
|
||||
} else {
|
||||
oo = OperationOutcomeUtil.newInstance(getContext());
|
||||
String message = getContext().getLocalizer().getMessage(BaseStorageDao.class, "successfulDeletes", deletedResources.size(), w.getMillis());
|
||||
String severity = "information";
|
||||
String code = "informational";
|
||||
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
|
||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulDeletes", deletedResources.size());
|
||||
msg += " " + getContext().getLocalizer().getMessageSanitized(BaseStorageDao.class, "successfulTimingSuffix", w.getMillis());
|
||||
oo = createInfoOperationOutcome(msg, StorageResponseCodeEnum.SUCCESSFUL_DELETE);
|
||||
}
|
||||
|
||||
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) {
|
||||
IBaseResource oldVersion = toResource(theEntity, false);
|
||||
IBaseResource oldVersion = myJpaStorageResourceParser.toResource(theEntity, false);
|
||||
|
||||
List<TagDefinition> tags = toTagList(theMetaAdd);
|
||||
for (TagDefinition nextDef : tags) {
|
||||
|
@ -780,7 +783,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
myEntityManager.merge(theEntity);
|
||||
|
||||
// Interceptor call: STORAGE_PRECOMMIT_RESOURCE_UPDATED
|
||||
IBaseResource newVersion = toResource(theEntity, false);
|
||||
IBaseResource newVersion = myJpaStorageResourceParser.toResource(theEntity, false);
|
||||
HookParams preStorageParams = new HookParams()
|
||||
.add(IBaseResource.class, oldVersion)
|
||||
.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) {
|
||||
|
||||
// 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);
|
||||
|
||||
|
@ -826,7 +829,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
theEntity = myEntityManager.merge(theEntity);
|
||||
|
||||
// Interceptor call: STORAGE_PRECOMMIT_RESOURCE_UPDATED
|
||||
IBaseResource newVersion = toResource(theEntity, false);
|
||||
IBaseResource newVersion = myJpaStorageResourceParser.toResource(theEntity, false);
|
||||
HookParams preStorageParams = new HookParams()
|
||||
.add(IBaseResource.class, oldVersion)
|
||||
.add(IBaseResource.class, newVersion)
|
||||
|
@ -891,6 +894,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
@ -910,7 +914,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
@Transactional
|
||||
public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) {
|
||||
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());
|
||||
return retVal;
|
||||
}
|
||||
|
@ -926,7 +930,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
|
||||
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());
|
||||
return retVal;
|
||||
|
@ -941,7 +945,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
IIdType id = theId.withResourceType(myResourceName).toUnqualifiedVersionless();
|
||||
BaseHasResource entity = readEntity(id, theRequest);
|
||||
|
||||
IBundleProvider retVal = super.history(theRequest, myResourceName, entity.getId(),
|
||||
IBundleProvider retVal = myPersistedJpaBundleProviderFactory.history(theRequest, myResourceName, entity.getId(),
|
||||
theHistorySearchDateRangeParam.getLowerBoundAsInstant(),
|
||||
theHistorySearchDateRangeParam.getUpperBoundAsInstant(),
|
||||
theHistorySearchDateRangeParam.getOffset(),
|
||||
|
@ -1098,68 +1102,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
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) {
|
||||
return entityToUpdate.getDeleted() != null;
|
||||
}
|
||||
|
@ -1209,7 +1151,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
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());
|
||||
return retVal;
|
||||
|
@ -1243,7 +1185,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
BaseHasResource entity = readEntity(theId, theRequest);
|
||||
validateResourceType(entity);
|
||||
|
||||
T retVal = toResource(myResourceType, entity, null, false);
|
||||
T retVal = myJpaStorageResourceParser.toResource(myResourceType, entity, null, false);
|
||||
|
||||
if (theDeletedOk == false) {
|
||||
if (isDeleted(entity)) {
|
||||
|
@ -1297,7 +1239,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
ResourceTable entity = entityOpt.get();
|
||||
try {
|
||||
T resource = (T) toResource(entity, false);
|
||||
T resource = (T) myJpaStorageResourceParser.toResource(entity, false);
|
||||
reindex(resource, entity);
|
||||
} catch (BaseServerResponseException | DataFormatException e) {
|
||||
myResourceTableDao.updateIndexStatus(entity.getId(), INDEX_STATUS_INDEXING_FAILED);
|
||||
|
@ -1379,6 +1321,13 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IBasePersistedResource readEntityLatestVersion(ResourcePersistentId thePersistentId, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||
return myEntityManager.find(ResourceTable.class, thePersistentId.getIdAsLong());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
protected ResourceTable readEntityLatestVersion(IIdType theId, RequestDetails theRequestDetails, TransactionDetails theTransactionDetails) {
|
||||
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) {
|
||||
StopWatch w = new StopWatch();
|
||||
|
||||
T resource = theResource;
|
||||
|
||||
preProcessResourceForStorage(resource);
|
||||
|
@ -1705,6 +1652,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
ResourceTable entity = null;
|
||||
|
||||
IIdType resourceId;
|
||||
RestOperationTypeEnum update = RestOperationTypeEnum.UPDATE;
|
||||
if (isNotBlank(theMatchUrl)) {
|
||||
Set<JpaPid> match = myMatchResourceUrlService.processMatchUrl(theMatchUrl, myResourceType, theTransactionDetails, theRequest, theResource)
|
||||
.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());
|
||||
resourceId = entity.getIdDt();
|
||||
} 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
|
||||
if (outcome.getPersistentId() != null) {
|
||||
|
@ -1755,86 +1704,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
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()) {
|
||||
throw new ResourceVersionConflictException(Msg.code(989) + "Trying to update " + resourceId + " but this is not the current version");
|
||||
}
|
||||
// Start
|
||||
|
||||
if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) {
|
||||
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;
|
||||
return doUpdateForUpdateOrPatch(theRequest, resourceId, theMatchUrl, thePerformIndexing, theForceUpdateVersion, resource, entity, update, theTransactionDetails);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
preProcessResourceForStorage(theResource, theRequest, theTransactionDetails, false);
|
||||
|
||||
BaseHasResource entity = null;
|
||||
BaseHasResource currentEntity = null;
|
||||
BaseHasResource entity;
|
||||
BaseHasResource currentEntity;
|
||||
|
||||
IIdType resourceId;
|
||||
|
||||
|
@ -1879,12 +1759,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
entity.setDeleted(null);
|
||||
boolean isUpdatingCurrent = resourceId.hasVersionIdPart() && Long.parseLong(resourceId.getVersionIdPart()) == currentEntity.getVersion();
|
||||
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());
|
||||
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
|
||||
populateOperationOutcomeForUpdate(w, outcome, null, RestOperationTypeEnum.UPDATE);
|
||||
|
||||
ourLog.debug(msg);
|
||||
return outcome;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
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.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
|
||||
import ca.uhn.fhir.jpa.api.model.ExpungeOutcome;
|
||||
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.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProviderFactory;
|
||||
import ca.uhn.fhir.jpa.search.builder.SearchBuilder;
|
||||
import ca.uhn.fhir.jpa.util.QueryChunker;
|
||||
import ca.uhn.fhir.jpa.util.ResourceCountCache;
|
||||
|
@ -26,6 +34,9 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.PersistenceContextType;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
|
@ -59,25 +70,36 @@ import java.util.stream.Collectors;
|
|||
* #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];
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
|
||||
public ResourceCountCache myResourceCountsCache;
|
||||
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
protected EntityManager myEntityManager;
|
||||
@Autowired
|
||||
private TransactionProcessor myTransactionProcessor;
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
@Autowired
|
||||
private ExpungeService myExpungeService;
|
||||
@Autowired
|
||||
private IResourceTableDao myResourceTableDao;
|
||||
@Autowired
|
||||
private PersistedJpaBundleProviderFactory myPersistedJpaBundleProviderFactory;
|
||||
@Autowired
|
||||
private IResourceTagDao myResourceTagDao;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTransactionProcessorForUnitTest(TransactionProcessor theTransactionProcessor) {
|
||||
myTransactionProcessor = theTransactionProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
super.start();
|
||||
myTransactionProcessor.setDao(this);
|
||||
}
|
||||
|
||||
|
@ -125,7 +147,7 @@ public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends B
|
|||
@Override
|
||||
public IBundleProvider history(Date theSince, Date theUntil, Integer theOffset, RequestDetails theRequestDetails) {
|
||||
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());
|
||||
return retVal;
|
||||
}
|
||||
|
@ -260,4 +282,25 @@ public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends B
|
|||
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%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
||||
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
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
|
||||
|
@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao.r4;
|
|||
*/
|
||||
|
||||
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;
|
||||
|
@ -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.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.r4.model.Composition;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
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
|
||||
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.searchparam.SearchParameterMap;
|
||||
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.SortSpec;
|
||||
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 ca.uhn.fhir.rest.param.ReferenceOrListParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
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.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.Date;
|
||||
import java.util.List;
|
||||
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
|
||||
ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
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,
|
||||
Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
|
||||
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.FhirVersionEnum;
|
||||
|
@ -6,18 +6,18 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
|
|||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
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.searchparam.extractor.ISearchParamExtractor;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
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.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.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
import org.hl7.fhir.r4.model.SearchParameter;
|
||||
import org.hl7.fhir.r5.model.CodeType;
|
||||
import org.hl7.fhir.r5.model.Enumerations;
|
||||
import org.hl7.fhir.r5.model.SearchParameter;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -46,60 +46,63 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* #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].*");
|
||||
@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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void postPersist(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
||||
protected void postPersist(ResourceTable theEntity, T theResource, RequestDetails theRequestDetails) {
|
||||
super.postPersist(theEntity, theResource, theRequestDetails);
|
||||
reindexAffectedResources(theResource, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource, RequestDetails theRequestDetails) {
|
||||
protected void postUpdate(ResourceTable theEntity, T theResource, RequestDetails theRequestDetails) {
|
||||
super.postUpdate(theEntity, theResource, theRequestDetails);
|
||||
reindexAffectedResources(theResource, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preDelete(SearchParameter theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
||||
protected void preDelete(T theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) {
|
||||
super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails);
|
||||
reindexAffectedResources(theResourceToDelete, theRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
|
||||
protected void validateResourceForStorage(T theResource, ResourceTable 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
|
||||
* doing that
|
||||
*/
|
||||
if (theDaoConfig.getModelConfig().isDefaultSearchParamsCanBeOverridden() == false) {
|
||||
for (IPrimitiveType<?> nextBaseType : theResource.getBase()) {
|
||||
for (IPrimitiveType<?> nextBaseType : searchParameter.getBase()) {
|
||||
String nextBase = nextBaseType.getValueAsString();
|
||||
RuntimeSearchParam existingSearchParam = theSearchParamRegistry.getActiveSearchParam(nextBase, theResource.getCode());
|
||||
RuntimeSearchParam existingSearchParam = mySearchParamRegistry.getActiveSearchParam(nextBase, searchParameter.getCode());
|
||||
if (existingSearchParam != null) {
|
||||
boolean isBuiltIn = existingSearchParam.getId() == null;
|
||||
isBuiltIn |= existingSearchParam.getUri().startsWith("http://hl7.org/fhir/SearchParameter/");
|
||||
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
|
||||
* 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");
|
||||
}
|
||||
if (!theResource.getStatus().name().equals("ACTIVE")) {
|
||||
if (!searchParameter.getStatus().name().equals("ACTIVE")) {
|
||||
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");
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
} else if (isBlank(theResource.getExpression())) {
|
||||
} else if (isBlank(searchParameter.getExpression())) {
|
||||
|
||||
throw new UnprocessableEntityException(Msg.code(1114) + "SearchParameter.expression is missing");
|
||||
|
||||
} else {
|
||||
|
||||
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");
|
||||
}
|
||||
for (SearchParameter.SearchParameterComponentComponent next : theResource.getComponent()) {
|
||||
for (SearchParameter.SearchParameterComponentComponent next : searchParameter.getComponent()) {
|
||||
if (isBlank(next.getDefinition())) {
|
||||
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();
|
||||
if (fhirVersion.isOlderThan(FhirVersionEnum.DSTU3)) {
|
||||
// omitting validation for DSTU2_HL7ORG, DSTU2_1 and DSTU2
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
|
||||
if (theDaoConfig.isValidateSearchParameterExpressionsOnSave()) {
|
||||
|
||||
validateExpressionPath(theResource);
|
||||
validateExpressionPath(searchParameter);
|
||||
|
||||
String expression = getExpression(theResource);
|
||||
String expression = getExpression(searchParameter);
|
||||
|
||||
try {
|
||||
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);
|
||||
|
||||
boolean isResourceOfTypeComposite = theSearchParameter.getType() == Enumerations.SearchParamType.COMPOSITE;
|
||||
|
@ -175,23 +182,23 @@ public class FhirResourceDaoSearchParameterR4 extends BaseHapiFhirResourceDao<Se
|
|||
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static String getExpression(SearchParameter theSearchParameter){
|
||||
private static String getExpression(SearchParameter theSearchParameter) {
|
||||
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);
|
||||
|
||||
return theSearchParameter
|
||||
.getExtensionsByUrl(HapiExtensions.EXT_SP_UNIQUE)
|
||||
.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.jpa.api.config.DaoConfig;
|
||||
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.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.IResourceHistoryProvenanceDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
|
||||
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.IResourceIndexedSearchParamUriDao;
|
||||
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.IResourceTagDao;
|
||||
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.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
|
@ -124,11 +124,13 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
|||
private DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
@Autowired
|
||||
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public List<ResourcePersistentId> findHistoricalVersionsOfNonDeletedResources(String theResourceName, ResourcePersistentId theResourceId, int theRemainingCount) {
|
||||
if(isEmptyQuery(theRemainingCount)){
|
||||
if (isEmptyQuery(theRemainingCount)) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
|
@ -156,7 +158,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
|||
@Override
|
||||
@Transactional
|
||||
public List<ResourcePersistentId> findHistoricalVersionsOfDeletedResources(String theResourceName, ResourcePersistentId theResourceId, int theRemainingCount) {
|
||||
if(isEmptyQuery(theRemainingCount)){
|
||||
if (isEmptyQuery(theRemainingCount)) {
|
||||
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
|
||||
* so it shouldn't be too big a deal.
|
||||
*/
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
myMemoryCacheService.invalidateAllCaches();
|
||||
|
@ -222,8 +224,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
|||
private void callHooks(RequestDetails theRequestDetails, AtomicInteger theRemainingCount, ResourceHistoryTable theVersion, IdDt theId) {
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
if (CompositeInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE, myInterceptorBroadcaster, theRequestDetails)) {
|
||||
IFhirResourceDao<?> resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
|
||||
IBaseResource resource = resourceDao.toResource(theVersion, false);
|
||||
IBaseResource resource = myJpaStorageResourceParser.toResource(theVersion, false);
|
||||
HookParams params = new HookParams()
|
||||
.add(AtomicInteger.class, counter)
|
||||
.add(IIdType.class, theId)
|
||||
|
@ -327,7 +328,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
|||
|
||||
private void expungeHistoricalVersionsOfId(RequestDetails theRequestDetails, Long myResourceId, AtomicInteger theRemainingCount) {
|
||||
Pageable page;
|
||||
synchronized (theRemainingCount){
|
||||
synchronized (theRemainingCount) {
|
||||
if (expungeLimitReached(theRemainingCount)) {
|
||||
return;
|
||||
}
|
||||
|
@ -351,7 +352,7 @@ public class ResourceExpungeService implements IResourceExpungeService {
|
|||
return new SliceImpl<>(Collections.singletonList(myVersion.getId()));
|
||||
}
|
||||
|
||||
private boolean isEmptyQuery(int theCount){
|
||||
private boolean isEmptyQuery(int theCount) {
|
||||
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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
@ -42,7 +43,7 @@ public class DeleteExpungeSvcImpl implements IDeleteExpungeSvc {
|
|||
private final DeleteExpungeSqlBuilder myDeleteExpungeSqlBuilder;
|
||||
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;
|
||||
myDeleteExpungeSqlBuilder = theDeleteExpungeSqlBuilder;
|
||||
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.dao.HistoryBuilder;
|
||||
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.SearchBuilderFactory;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
|
@ -104,13 +105,15 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
private RequestPartitionHelperSvc myRequestPartitionHelperSvc;
|
||||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
@Autowired
|
||||
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||
|
||||
/*
|
||||
* Non autowired fields (will be different for every instance
|
||||
* of this class, since it's a prototype
|
||||
*/
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
private Search mySearchEntity;
|
||||
private String myUuid;
|
||||
private SearchCacheStatusEnum myCacheStatus;
|
||||
|
@ -162,7 +165,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
resource = next;
|
||||
|
||||
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.dao.ISearchBuilder;
|
||||
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.rest.api.server.IBundleProvider;
|
||||
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.context.ApplicationContext;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
|
||||
public class PersistedJpaBundleProviderFactory {
|
||||
|
||||
@Autowired
|
||||
|
@ -46,4 +55,28 @@ public class PersistedJpaBundleProviderFactory {
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
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.dao.BaseStorageDao;
|
||||
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.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
||||
import ca.uhn.fhir.jpa.dao.search.ResourceNotFoundInIndexException;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||
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.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||
|
@ -145,20 +146,34 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
@Deprecated
|
||||
public static final int MAXIMUM_PAGE_SIZE = SearchConstants.MAX_PAGE_SIZE;
|
||||
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 JpaPid NO_MORE = new JpaPid(-1L);
|
||||
private static final String MY_TARGET_RESOURCE_PID = "myTargetResourcePid";
|
||||
private static final String MY_SOURCE_RESOURCE_PID = "mySourceResourcePid";
|
||||
private static final String MY_TARGET_RESOURCE_TYPE = "myTargetResourceType";
|
||||
|
||||
private static final String MY_SOURCE_RESOURCE_TYPE = "mySourceResourceType";
|
||||
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;
|
||||
protected final IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
protected final IResourceTagDao myResourceTagDao;
|
||||
private final String myResourceName;
|
||||
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 CriteriaBuilder myCriteriaBuilder;
|
||||
private SearchParameterMap myParams;
|
||||
|
@ -168,30 +183,12 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private Set<ResourcePersistentId> myPidSet;
|
||||
private boolean myHasNextIteratorQuery = false;
|
||||
private RequestPartitionId myRequestPartitionId;
|
||||
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
protected EntityManager myEntityManager;
|
||||
@Autowired(required = false)
|
||||
private IFulltextSearchSvc myFulltextSearchSvc;
|
||||
@Autowired(required = false)
|
||||
private IElasticsearchSvc myIElasticsearchSvc;
|
||||
|
||||
private final HapiFhirLocalContainerEntityManagerFactoryBean myEntityManagerFactory;
|
||||
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;
|
||||
@Autowired
|
||||
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -894,7 +891,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
IBaseResource resource = 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) {
|
||||
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.IConnectionPoolInfoProvider;
|
||||
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.ITermCodeSystemVersionDao;
|
||||
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.TermValueSetConcept;
|
||||
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.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
||||
|
@ -265,6 +266,8 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
private CachingValidationSupport myCachingValidationSupport;
|
||||
@Autowired
|
||||
private VersionCanonicalizer myVersionCanonicalizer;
|
||||
@Autowired
|
||||
private IJpaStorageResourceParser myJpaStorageResourceParser;
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||
|
@ -2437,7 +2440,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
+ ForcedId.IDX_FORCEDID_TYPE_FID + " removed?");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -2526,7 +2529,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
private org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -44,12 +44,6 @@ public class BaseCqlR4Test extends BaseJpaR4Test implements CqlProviderTestBase
|
|||
@RegisterExtension
|
||||
protected PartitionHelper myPartitionHelper;
|
||||
|
||||
// FIXME: restore?
|
||||
// @Override
|
||||
// public void beforeResetInterceptors() {
|
||||
// myInterceptorRegistry.unregisterInterceptorsIf(t->!(t instanceof PartitionHelper.MyTestInterceptor));
|
||||
// }
|
||||
|
||||
@Autowired
|
||||
protected
|
||||
DaoRegistry myDaoRegistry;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -20,14 +20,15 @@ package ca.uhn.fhir.jpa.model.cross;
|
|||
* #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 java.util.Date;
|
||||
|
||||
public interface IBasePersistedResource extends IResourceLookup {
|
||||
|
||||
IIdType getIdDt();
|
||||
|
||||
long getVersion();
|
||||
|
||||
boolean isDeleted();
|
||||
|
||||
void setNotDeleted();
|
||||
}
|
||||
|
|
|
@ -20,9 +20,10 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.context.ParserOptions;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu2.model.Subscription;
|
||||
|
@ -79,6 +80,30 @@ public class ModelConfig {
|
|||
private boolean myDefaultSearchParamsCanBeOverridden = true;
|
||||
private Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>();
|
||||
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 myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH;
|
||||
/**
|
||||
|
|
|
@ -213,6 +213,16 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
|
|||
return myResourceVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return getDeleted() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotDeleted() {
|
||||
setDeleted(null);
|
||||
}
|
||||
|
||||
public void setVersion(long theVersion) {
|
||||
myResourceVersion = theVersion;
|
||||
}
|
||||
|
|
|
@ -549,6 +549,16 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
|
|||
return myVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeleted() {
|
||||
return getDeleted() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotDeleted() {
|
||||
setDeleted(null);
|
||||
}
|
||||
|
||||
public void setVersion(long theVersion) {
|
||||
myVersion = theVersion;
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.searchparam.registry;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.context.ComboSearchParamType;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.context.phonetic.IPhoneticEncoder;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -179,33 +179,35 @@ public class SearchParameterCanonicalizer {
|
|||
String path = theNextSp.getExpression();
|
||||
RestSearchParameterTypeEnum paramType = null;
|
||||
RuntimeSearchParam.RuntimeSearchParamStatusEnum status = null;
|
||||
switch (theNextSp.getType()) {
|
||||
case COMPOSITE:
|
||||
paramType = RestSearchParameterTypeEnum.COMPOSITE;
|
||||
break;
|
||||
case DATE:
|
||||
paramType = RestSearchParameterTypeEnum.DATE;
|
||||
break;
|
||||
case NUMBER:
|
||||
paramType = RestSearchParameterTypeEnum.NUMBER;
|
||||
break;
|
||||
case QUANTITY:
|
||||
paramType = RestSearchParameterTypeEnum.QUANTITY;
|
||||
break;
|
||||
case REFERENCE:
|
||||
paramType = RestSearchParameterTypeEnum.REFERENCE;
|
||||
break;
|
||||
case STRING:
|
||||
paramType = RestSearchParameterTypeEnum.STRING;
|
||||
break;
|
||||
case TOKEN:
|
||||
paramType = RestSearchParameterTypeEnum.TOKEN;
|
||||
break;
|
||||
case URI:
|
||||
paramType = RestSearchParameterTypeEnum.URI;
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
if (theNextSp.getType() != null) {
|
||||
switch (theNextSp.getType()) {
|
||||
case COMPOSITE:
|
||||
paramType = RestSearchParameterTypeEnum.COMPOSITE;
|
||||
break;
|
||||
case DATE:
|
||||
paramType = RestSearchParameterTypeEnum.DATE;
|
||||
break;
|
||||
case NUMBER:
|
||||
paramType = RestSearchParameterTypeEnum.NUMBER;
|
||||
break;
|
||||
case QUANTITY:
|
||||
paramType = RestSearchParameterTypeEnum.QUANTITY;
|
||||
break;
|
||||
case REFERENCE:
|
||||
paramType = RestSearchParameterTypeEnum.REFERENCE;
|
||||
break;
|
||||
case STRING:
|
||||
paramType = RestSearchParameterTypeEnum.STRING;
|
||||
break;
|
||||
case TOKEN:
|
||||
paramType = RestSearchParameterTypeEnum.TOKEN;
|
||||
break;
|
||||
case URI:
|
||||
paramType = RestSearchParameterTypeEnum.URI;
|
||||
break;
|
||||
case NULL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (theNextSp.getStatus() != null) {
|
||||
switch (theNextSp.getStatus()) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
|||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
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.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
|
@ -94,7 +95,12 @@ public class SubscriptionValidatingInterceptor {
|
|||
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;
|
||||
if (subscription.getStatus() == null) {
|
||||
throw new UnprocessableEntityException(Msg.code(8) + "Can not process submitted Subscription - Subscription.status must be populated on this server");
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</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.subscription.match.registry.SubscriptionLoader;
|
||||
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.util.ResourceCountCache;
|
||||
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.BeforeEach;
|
||||
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.Qualifier;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
@ -213,6 +215,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
@Autowired
|
||||
private ValidationSupportChain myJpaValidationSupportChain;
|
||||
|
||||
@RegisterExtension
|
||||
private final PreventDanglingInterceptorsExtension myPreventDanglingInterceptorsExtension = new PreventDanglingInterceptorsExtension(()-> myInterceptorRegistry);
|
||||
|
||||
@BeforeEach
|
||||
public void beforeFlushFT() {
|
||||
purgeHibernateSearch(myEntityManager);
|
||||
|
@ -232,13 +237,6 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
|
|||
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
|
||||
}
|
||||
|
||||
@Override
|
||||
@AfterEach
|
||||
public void afterResetInterceptors() {
|
||||
super.afterResetInterceptors();
|
||||
myInterceptorRegistry.unregisterAllInterceptors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
|
|
|
@ -58,6 +58,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
Subscription subs = new Subscription();
|
||||
subs.getChannel().setType(SubscriptionChannelTypeEnum.REST_HOOK);
|
||||
subs.setCriteria("Observation?identifier=123");
|
||||
subs.getChannel().setEndpoint("http://localhost");
|
||||
try {
|
||||
myClient.create().resource(subs).execute();
|
||||
fail();
|
||||
|
@ -105,6 +106,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
public void testCreateWithPopulatedButInvalidStatue() {
|
||||
Subscription subs = new Subscription();
|
||||
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
|
||||
subs.getChannel().setEndpoint("http://localhost");
|
||||
subs.setCriteria("Observation?identifier=123");
|
||||
subs.getStatusElement().setValue("aaaaa");
|
||||
|
||||
|
@ -112,7 +114,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
myClient.create().resource(subs).execute();
|
||||
fail();
|
||||
} 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();
|
||||
subs.getChannel().setType(SubscriptionChannelTypeEnum.REST_HOOK);
|
||||
subs.setCriteria("Observation?identifier=123");
|
||||
subs.getChannel().setEndpoint("http://localhost");
|
||||
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
|
||||
IIdType id = myClient.create().resource(subs).execute().getId();
|
||||
subs.setId(id);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSpWithMultiplePaths(){
|
||||
public void testCreateSpWithMultiplePaths() {
|
||||
SearchParameter sp = new SearchParameter();
|
||||
sp.setCode("telephone-unformatted");
|
||||
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
|
@ -101,18 +101,56 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
sp.getBase().add(new CodeType("Patient"));
|
||||
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||
|
||||
DaoMethodOutcome daoMethodOutcome = mySearchParameterDao.create(sp);
|
||||
DaoMethodOutcome daoMethodOutcome;
|
||||
|
||||
daoMethodOutcome = mySearchParameterDao.create(sp);
|
||||
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.setCode("telephone-unformatted-2");
|
||||
daoMethodOutcome = mySearchParameterDao.create(sp);
|
||||
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.setCode("telephone-unformatted-3");
|
||||
daoMethodOutcome = mySearchParameterDao.create(sp);
|
||||
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.setCode("telephone-unformatted-3");
|
||||
|
@ -120,7 +158,6 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
assertThat(daoMethodOutcome.getId(), is(notNullValue()));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateInvalidParamNoPath() {
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
|
@ -1110,21 +1147,21 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
medAdmin.setMedication(new Reference(medication));
|
||||
|
||||
myMedicationAdministrationDao.create(medAdmin);
|
||||
|
||||
runInTransaction(()->{
|
||||
|
||||
runInTransaction(() -> {
|
||||
List<ResourceIndexedSearchParamToken> tokens = myResourceIndexedSearchParamTokenDao
|
||||
.findAll()
|
||||
.stream()
|
||||
.filter(t -> t.getParamName().equals("medicationadministration-ingredient-medication"))
|
||||
.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(false, tokens.get(0).isMissing());
|
||||
|
||||
});
|
||||
|
||||
SearchParameterMap map = SearchParameterMap.newSynchronous();
|
||||
map.add("medicationadministration-ingredient-medication", new TokenParam("system","code"));
|
||||
map.add("medicationadministration-ingredient-medication", new TokenParam("system", "code"));
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider search = myMedicationAdministrationDao.search(map);
|
||||
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.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.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
|
||||
|
|
|
@ -1117,7 +1117,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
String encoded = myFhirContext.newXmlParser().encodeResourceToString(response.getOperationOutcome());
|
||||
ourLog.info(encoded);
|
||||
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 {
|
||||
myClient.read().resource("Patient").withId(id1).execute();
|
||||
fail();
|
||||
|
@ -1144,7 +1144,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
ourLog.info(response);
|
||||
assertEquals(200, resp.getStatusLine().getStatusCode());
|
||||
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 {
|
||||
IOUtils.closeQuietly(resp);
|
||||
}
|
||||
|
@ -1214,7 +1214,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(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 {
|
||||
response.close();
|
||||
}
|
||||
|
@ -1241,7 +1241,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(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 {
|
||||
response.close();
|
||||
}
|
||||
|
@ -1322,7 +1322,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
MethodOutcome resp = myClient.delete().resourceById(id).execute();
|
||||
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
|
||||
public void testPatchUsingJsonPatch_Transaction() throws Exception {
|
||||
String methodName = "testPatchUsingJsonPatch_Transaction";
|
||||
public void testPatchUsingJsonPatch_Transaction() {
|
||||
IIdType pid1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("0");
|
||||
patient.addName().setFamily(methodName).addGiven("Joe");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
|
@ -224,6 +221,7 @@ public class SystemProviderTransactionSearchDstu3Test extends BaseJpaDstu3Test {
|
|||
.getRequest().setUrl(pid1.getValue()).setMethod(HTTPVerb.PUT);
|
||||
|
||||
Bundle bundle = ourClient.transaction().withBundle(input).execute();
|
||||
ourLog.info("Response: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||
|
||||
//Validate over all bundle response entry contents.
|
||||
assertThat(bundle.getType(), is(equalTo(Bundle.BundleType.TRANSACTIONRESPONSE)));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<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>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -404,18 +404,6 @@ public class Batch2CoordinatorIT extends BaseJpaR4Test {
|
|||
|
||||
@Test
|
||||
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
|
||||
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