Fix #148 - Add _count support to everything operation
This commit is contained in:
parent
8c37973a78
commit
11507ef97c
|
@ -246,12 +246,11 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
|
||||||
|
|
||||||
boolean respondGzip = theRequest.isRespondGzip();
|
|
||||||
|
|
||||||
HttpServletResponse response = theRequest.getServletResponse();
|
|
||||||
Object resultObj = invokeServer(theRequest, params);
|
Object resultObj = invokeServer(theRequest, params);
|
||||||
|
|
||||||
|
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||||
|
boolean respondGzip = theRequest.isRespondGzip();
|
||||||
|
HttpServletResponse response = theRequest.getServletResponse();
|
||||||
switch (getReturnType()) {
|
switch (getReturnType()) {
|
||||||
case BUNDLE: {
|
case BUNDLE: {
|
||||||
|
|
||||||
|
@ -283,12 +282,15 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
||||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
|
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
Set<Include> includes = getRequestIncludesFromParams(params);
|
Set<Include> includes = getRequestIncludesFromParams(params);
|
||||||
|
|
||||||
IBundleProvider result = (IBundleProvider) resultObj;
|
IBundleProvider result = (IBundleProvider) resultObj;
|
||||||
|
if (count == null) {
|
||||||
|
count = result.preferredPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
||||||
bundleFactory.initializeBundleFromBundleProvider(theServer, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, 0, count, null,
|
bundleFactory.initializeBundleFromBundleProvider(theServer, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, 0, count, null, getResponseBundleType(), includes);
|
||||||
getResponseBundleType(), includes);
|
|
||||||
Bundle bundle = bundleFactory.getDstu1Bundle();
|
Bundle bundle = bundleFactory.getDstu1Bundle();
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
|
|
|
@ -208,6 +208,11 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
public int size() {
|
public int size() {
|
||||||
return resources.size();
|
return resources.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,11 @@ public class BundleProviders {
|
||||||
public InstantDt getPublished() {
|
public InstantDt getPublished() {
|
||||||
return published;
|
return published;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,21 +26,24 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.*;
|
|
||||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.IBaseResource;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.Include;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||||
|
|
||||||
public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
||||||
|
|
||||||
|
@ -253,7 +256,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseResource getResourceBundle() {
|
public IResource getResourceBundle() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,37 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
public interface IBundleProvider {
|
public interface IBundleProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Load the given collection of resources by index, plus any additional resources per the
|
||||||
|
* server's processing rules (e.g. _include'd resources, OperationOutcome, etc.). For example,
|
||||||
|
* if the method is invoked with index 0,10 the method might return 10 search results, plus an
|
||||||
|
* additional 20 resources which matched a client's _include specification.
|
||||||
|
*
|
||||||
* @param theFromIndex The low index (inclusive) to return
|
* @param theFromIndex The low index (inclusive) to return
|
||||||
* @param theToIndex The high index (exclusive) to return
|
* @param theToIndex The high index (exclusive) to return
|
||||||
* @return A list of resources. The size of this list must be at least <code>theToIndex - theFromIndex</code>.
|
* @return A list of resources. The size of this list must be at least <code>theToIndex - theFromIndex</code>.
|
||||||
*/
|
*/
|
||||||
List<IResource> getResources(int theFromIndex, int theToIndex);
|
List<IResource> getResources(int theFromIndex, int theToIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally may be used to signal a preferred page size to the server, e.g. because
|
||||||
|
* the implementing code recognizes that the resources which will be returned by this
|
||||||
|
* implementation are expensive to load so a smaller page size should be used. The value
|
||||||
|
* returned by this method will only be used if the client has not explicitly requested
|
||||||
|
* a page size.
|
||||||
|
*
|
||||||
|
* @return Returns the preferred page size or <code>null</code>
|
||||||
|
*/
|
||||||
|
Integer preferredPageSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of results which match the given query (exclusive of any
|
||||||
|
* _include's or OperationOutcome)
|
||||||
|
*/
|
||||||
int size();
|
int size();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the instant as of which this result was valid
|
||||||
|
*/
|
||||||
InstantDt getPublished();
|
InstantDt getPublished();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,9 @@ package ca.uhn.fhir.rest.server;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.Include;
|
|
||||||
import org.hl7.fhir.instance.model.IBaseResource;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.Include;
|
||||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,11 +39,11 @@ public interface IVersionSpecificBundleFactory {
|
||||||
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType);
|
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType);
|
||||||
|
|
||||||
void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
|
void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
|
||||||
int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes);
|
int theOffset, Integer theCount, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes);
|
||||||
|
|
||||||
Bundle getDstu1Bundle();
|
Bundle getDstu1Bundle();
|
||||||
|
|
||||||
IBaseResource getResourceBundle();
|
IResource getResourceBundle();
|
||||||
|
|
||||||
void initializeBundleFromResourceList(String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType);
|
void initializeBundleFromResourceList(String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.hl7.fhir.instance.model.IBaseResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by {@link IBundleProvider} to provide a single page worth of results.
|
||||||
|
*
|
||||||
|
* If the server chooses to, it may return a different number of matching results to the number that the user requested.
|
||||||
|
* For example, if the client requested 100 results but the server decided to return only 10 (perhaps because they were
|
||||||
|
* very large), this value should be set to 10. Note that this count refers only to resources which are included in the
|
||||||
|
* indexes provided to {@link IBundleProvider#getResources(int, int)}, so it should not reflect any additional results
|
||||||
|
* added to the response as a result of _include parameters, OperationOutcome's etc.
|
||||||
|
*/
|
||||||
|
public class ResponseResourceList {
|
||||||
|
/**
|
||||||
|
* Singleton unmodifiable empty list
|
||||||
|
*/
|
||||||
|
public static final ResponseResourceList EMPTY = new EmptyResponseResourceList();
|
||||||
|
|
||||||
|
private List<IBaseResource> myIncludeResults;
|
||||||
|
private List<IBaseResource> myMatchResults;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an "include" results. Include results are results which are added as a result of <code>_include</code>
|
||||||
|
* directives in search requests.
|
||||||
|
*/
|
||||||
|
public void addIncludeResults(IBaseResource theIncludeResult) {
|
||||||
|
if (myIncludeResults == null) {
|
||||||
|
myIncludeResults = new ArrayList<IBaseResource>();
|
||||||
|
}
|
||||||
|
myIncludeResults.add(theIncludeResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a "match" result. A match result is a result added as a direct result of the operation in question. E.g. for
|
||||||
|
* a search invocation a match result would be a result which directly matched the search criteria. For a history
|
||||||
|
* invocation it would be a historical version of a resource or the current version.
|
||||||
|
*/
|
||||||
|
public void addMatchResult(IBaseResource theResource) {
|
||||||
|
Validate.notNull(theResource, "theResource must not be null");
|
||||||
|
if (myMatchResults == null) {
|
||||||
|
myMatchResults = new ArrayList<IBaseResource>();
|
||||||
|
}
|
||||||
|
myMatchResults.add(theResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IBaseResource> getIncludeResults() {
|
||||||
|
return myIncludeResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IBaseResource> getMatchResults() {
|
||||||
|
return myMatchResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the "include" results. Include results are results which are added as a result of <code>_include</code>
|
||||||
|
* directives in search requests.
|
||||||
|
*/
|
||||||
|
public void setIncludeResults(List<IBaseResource> theIncludeResults) {
|
||||||
|
myIncludeResults = theIncludeResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the "match" results. A match result is a result added as a direct result of the operation in question. E.g.
|
||||||
|
* for a search invocation a match result would be a result which directly matched the search criteria. For a
|
||||||
|
* history invocation it would be a historical version of a resource or the current version.
|
||||||
|
*/
|
||||||
|
public void setMatchResults(List<IBaseResource> theMatchResults) {
|
||||||
|
myMatchResults = theMatchResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class EmptyResponseResourceList extends ResponseResourceList {
|
||||||
|
@Override
|
||||||
|
public void addIncludeResults(IBaseResource theIncludeResult) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addMatchResult(IBaseResource theResource) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIncludeResults(List<IBaseResource> theIncludeResults) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setMatchResults(List<IBaseResource> theMatchResults) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -60,4 +60,9 @@ public class SimpleBundleProvider implements IBundleProvider {
|
||||||
return InstantDt.withCurrentTime();
|
return InstantDt.withCurrentTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -494,6 +494,11 @@ public abstract class BaseFhirDao implements IDao {
|
||||||
public int size() {
|
public int size() {
|
||||||
return tuples.size();
|
return tuples.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -659,6 +659,18 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
return new HashSet<Long>(q.getResultList());
|
return new HashSet<Long>(q.getResultList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<IResource> addResourcesAsIncludesById(List<IResource> theListToPopulate, Set<IdDt> includePids, List<IResource> resources) {
|
||||||
|
if (!includePids.isEmpty()) {
|
||||||
|
ourLog.info("Loading {} included resources", includePids.size());
|
||||||
|
resources = loadResourcesById(includePids);
|
||||||
|
for (IResource next : resources) {
|
||||||
|
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(next, BundleEntrySearchModeEnum.INCLUDE);
|
||||||
|
}
|
||||||
|
theListToPopulate.addAll(resources);
|
||||||
|
}
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTag(IdDt theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
|
public void addTag(IdDt theId, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
|
@ -713,50 +725,6 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
return doCreate(theResource, theIfNoneExist, thePerformIndexing);
|
return doCreate(theResource, theIfNoneExist, thePerformIndexing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing) {
|
|
||||||
StopWatch w = new StopWatch();
|
|
||||||
ResourceTable entity = new ResourceTable();
|
|
||||||
entity.setResourceType(toResourceName(theResource));
|
|
||||||
|
|
||||||
if (isNotBlank(theIfNoneExist)) {
|
|
||||||
Set<Long> match = processMatchUrl(theIfNoneExist, myResourceType);
|
|
||||||
if (match.size() > 1) {
|
|
||||||
String msg = getContext().getLocalizer().getMessage(BaseFhirDao.class, "transactionOperationWithMultipleMatchFailure", "CREATE", theIfNoneExist, match.size());
|
|
||||||
throw new PreconditionFailedException(msg);
|
|
||||||
} else if (match.size() == 1) {
|
|
||||||
Long pid = match.iterator().next();
|
|
||||||
entity = myEntityManager.find(ResourceTable.class, pid);
|
|
||||||
return toMethodOutcome(entity, theResource).setCreated(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theResource.getId().isEmpty() == false) {
|
|
||||||
if (isValidPid(theResource.getId())) {
|
|
||||||
throw new UnprocessableEntityException(
|
|
||||||
"This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
|
|
||||||
}
|
|
||||||
createForcedIdIfNeeded(entity, theResource.getId());
|
|
||||||
|
|
||||||
if (entity.getForcedId() != null) {
|
|
||||||
try {
|
|
||||||
translateForcedIdToPid(theResource.getId());
|
|
||||||
throw new UnprocessableEntityException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "duplicateCreateForcedId", theResource.getId().getIdPart()));
|
|
||||||
} catch (ResourceNotFoundException e) {
|
|
||||||
// good, this ID doesn't exist so we can create it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
updateEntity(theResource, entity, false, null, thePerformIndexing, true);
|
|
||||||
|
|
||||||
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
|
|
||||||
|
|
||||||
notifyWriteCompleted();
|
|
||||||
ourLog.info("Processed create on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
|
||||||
return outcome;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
|
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
|
||||||
Predicate retVal = null;
|
Predicate retVal = null;
|
||||||
switch (left.getParamType()) {
|
switch (left.getParamType()) {
|
||||||
|
@ -979,6 +947,50 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
return toMethodOutcome(savedEntity, null);
|
return toMethodOutcome(savedEntity, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing) {
|
||||||
|
StopWatch w = new StopWatch();
|
||||||
|
ResourceTable entity = new ResourceTable();
|
||||||
|
entity.setResourceType(toResourceName(theResource));
|
||||||
|
|
||||||
|
if (isNotBlank(theIfNoneExist)) {
|
||||||
|
Set<Long> match = processMatchUrl(theIfNoneExist, myResourceType);
|
||||||
|
if (match.size() > 1) {
|
||||||
|
String msg = getContext().getLocalizer().getMessage(BaseFhirDao.class, "transactionOperationWithMultipleMatchFailure", "CREATE", theIfNoneExist, match.size());
|
||||||
|
throw new PreconditionFailedException(msg);
|
||||||
|
} else if (match.size() == 1) {
|
||||||
|
Long pid = match.iterator().next();
|
||||||
|
entity = myEntityManager.find(ResourceTable.class, pid);
|
||||||
|
return toMethodOutcome(entity, theResource).setCreated(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theResource.getId().isEmpty() == false) {
|
||||||
|
if (isValidPid(theResource.getId())) {
|
||||||
|
throw new UnprocessableEntityException(
|
||||||
|
"This server cannot create an entity with a user-specified numeric ID - Client should not specify an ID when creating a new resource, or should include at least one letter in the ID to force a client-defined ID");
|
||||||
|
}
|
||||||
|
createForcedIdIfNeeded(entity, theResource.getId());
|
||||||
|
|
||||||
|
if (entity.getForcedId() != null) {
|
||||||
|
try {
|
||||||
|
translateForcedIdToPid(theResource.getId());
|
||||||
|
throw new UnprocessableEntityException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "duplicateCreateForcedId", theResource.getId().getIdPart()));
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
// good, this ID doesn't exist so we can create it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEntity(theResource, entity, false, null, thePerformIndexing, true);
|
||||||
|
|
||||||
|
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
|
||||||
|
|
||||||
|
notifyWriteCompleted();
|
||||||
|
ourLog.info("Processed create on {} in {}ms", myResourceName, w.getMillisAndRestart());
|
||||||
|
return outcome;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TagList getAllResourceTags() {
|
public TagList getAllResourceTags() {
|
||||||
StopWatch w = new StopWatch();
|
StopWatch w = new StopWatch();
|
||||||
|
@ -987,6 +999,8 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract List<Object> getIncludeValues(FhirTerser theTerser, Include theInclude, IResource theResource, RuntimeResourceDefinition theResourceDef);
|
||||||
|
|
||||||
public Class<T> getResourceType() {
|
public Class<T> getResourceType() {
|
||||||
return myResourceType;
|
return myResourceType;
|
||||||
}
|
}
|
||||||
|
@ -1092,6 +1106,11 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return count;
|
return count;
|
||||||
|
@ -1140,6 +1159,159 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void loadReverseIncludes(List<Long> theMatches, Set<Include> theRevIncludes) {
|
||||||
|
if (theMatches.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashSet<Long> pidsToInclude = new HashSet<Long>();
|
||||||
|
|
||||||
|
for (Include nextInclude : theRevIncludes) {
|
||||||
|
boolean matchAll = "*".equals(nextInclude.getValue());
|
||||||
|
if (matchAll) {
|
||||||
|
String sql = "SELECT r FROM ResourceLink r WHERE r.myTargetResourcePid IN (:target_pids)";
|
||||||
|
TypedQuery<ResourceLink> q = myEntityManager.createQuery(sql, ResourceLink.class);
|
||||||
|
q.setParameter("target_pids", theMatches);
|
||||||
|
List<ResourceLink> results = q.getResultList();
|
||||||
|
for (ResourceLink resourceLink : results) {
|
||||||
|
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int colonIdx = nextInclude.getValue().indexOf(':');
|
||||||
|
if (colonIdx < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String resType = nextInclude.getValue().substring(0, colonIdx);
|
||||||
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(resType);
|
||||||
|
if (def == null) {
|
||||||
|
ourLog.warn("Unknown resource type in _revinclude=" + nextInclude.getValue());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String paramName = nextInclude.getValue().substring(colonIdx + 1);
|
||||||
|
RuntimeSearchParam param = def.getSearchParam(paramName);
|
||||||
|
if (param == null) {
|
||||||
|
ourLog.warn("Unknown param name in _revinclude=" + nextInclude.getValue());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String nextPath : param.getPathsSplit()) {
|
||||||
|
String sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r.myTargetResourcePid IN (:target_pids)";
|
||||||
|
TypedQuery<ResourceLink> q = myEntityManager.createQuery(sql, ResourceLink.class);
|
||||||
|
q.setParameter("src_path", nextPath);
|
||||||
|
q.setParameter("target_pids", theMatches);
|
||||||
|
List<ResourceLink> results = q.getResultList();
|
||||||
|
for (ResourceLink resourceLink : results) {
|
||||||
|
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
theMatches.addAll(pidsToInclude);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetaDt metaAddOperation(IdDt theResourceId, MetaDt theMetaAdd) {
|
||||||
|
StopWatch w = new StopWatch();
|
||||||
|
BaseHasResource entity = readEntity(theResourceId);
|
||||||
|
if (entity == null) {
|
||||||
|
throw new ResourceNotFoundException(theResourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TagDefinition> tags = toTagList(theMetaAdd);
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
for (TagDefinition nextDef : tags) {
|
||||||
|
|
||||||
|
boolean hasTag = false;
|
||||||
|
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
|
||||||
|
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
|
||||||
|
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
|
||||||
|
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
|
||||||
|
hasTag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasTag) {
|
||||||
|
entity.setHasTags(true);
|
||||||
|
|
||||||
|
TagDefinition def = getTag(nextDef.getTagType(), nextDef.getSystem(), nextDef.getCode(), nextDef.getDisplay());
|
||||||
|
BaseTag newEntity = entity.addTag(def);
|
||||||
|
myEntityManager.persist(newEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
myEntityManager.merge(entity);
|
||||||
|
notifyWriteCompleted();
|
||||||
|
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[] { theResourceId, w.getMillisAndRestart() });
|
||||||
|
|
||||||
|
return metaGetOperation(theResourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetaDt metaDeleteOperation(IdDt theResourceId, MetaDt theMetaDel) {
|
||||||
|
StopWatch w = new StopWatch();
|
||||||
|
BaseHasResource entity = readEntity(theResourceId);
|
||||||
|
if (entity == null) {
|
||||||
|
throw new ResourceNotFoundException(theResourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TagDefinition> tags = toTagList(theMetaDel);
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
for (TagDefinition nextDef : tags) {
|
||||||
|
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
|
||||||
|
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
|
||||||
|
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
|
||||||
|
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
|
||||||
|
myEntityManager.remove(next);
|
||||||
|
entity.getTags().remove(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
if (entity.getTags().isEmpty()) {
|
||||||
|
entity.setHasTags(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
myEntityManager.merge(entity);
|
||||||
|
|
||||||
|
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
|
||||||
|
|
||||||
|
return metaGetOperation(theResourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetaDt metaGetOperation() {
|
||||||
|
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)";
|
||||||
|
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
|
||||||
|
q.setParameter("res_type", myResourceName);
|
||||||
|
List<TagDefinition> tagDefinitions = q.getResultList();
|
||||||
|
|
||||||
|
MetaDt retVal = super.toMetaDt(tagDefinitions);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetaDt metaGetOperation(IdDt theId) {
|
||||||
|
Long pid = super.translateForcedIdToPid(theId);
|
||||||
|
|
||||||
|
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type AND t.myResourceId = :res_id)";
|
||||||
|
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
|
||||||
|
q.setParameter("res_type", myResourceName);
|
||||||
|
q.setParameter("res_id", pid);
|
||||||
|
List<TagDefinition> tagDefinitions = q.getResultList();
|
||||||
|
|
||||||
|
MetaDt retVal = super.toMetaDt(tagDefinitions);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void postConstruct() {
|
public void postConstruct() {
|
||||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(myResourceType);
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(myResourceType);
|
||||||
|
@ -1341,7 +1513,6 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
}
|
}
|
||||||
|
|
||||||
IBundleProvider retVal = new IBundleProvider() {
|
IBundleProvider retVal = new IBundleProvider() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InstantDt getPublished() {
|
public InstantDt getPublished() {
|
||||||
return now;
|
return now;
|
||||||
|
@ -1422,6 +1593,11 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer preferredPageSize() {
|
||||||
|
return theParams.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return pids.size();
|
return pids.size();
|
||||||
|
@ -1433,77 +1609,11 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<IResource> addResourcesAsIncludesById(List<IResource> theListToPopulate, Set<IdDt> includePids, List<IResource> resources) {
|
|
||||||
if (!includePids.isEmpty()) {
|
|
||||||
ourLog.info("Loading {} included resources", includePids.size());
|
|
||||||
resources = loadResourcesById(includePids);
|
|
||||||
for (IResource next : resources) {
|
|
||||||
ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(next, BundleEntrySearchModeEnum.INCLUDE);
|
|
||||||
}
|
|
||||||
theListToPopulate.addAll(resources);
|
|
||||||
}
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void loadReverseIncludes(List<Long> theMatches, Set<Include> theRevIncludes) {
|
|
||||||
if (theMatches.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSet<Long> pidsToInclude = new HashSet<Long>();
|
|
||||||
|
|
||||||
for (Include nextInclude : theRevIncludes) {
|
|
||||||
boolean matchAll = "*".equals(nextInclude.getValue());
|
|
||||||
if (matchAll) {
|
|
||||||
String sql = "SELECT r FROM ResourceLink r WHERE r.myTargetResourcePid IN (:target_pids)";
|
|
||||||
TypedQuery<ResourceLink> q = myEntityManager.createQuery(sql, ResourceLink.class);
|
|
||||||
q.setParameter("target_pids", theMatches);
|
|
||||||
List<ResourceLink> results = q.getResultList();
|
|
||||||
for (ResourceLink resourceLink : results) {
|
|
||||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int colonIdx = nextInclude.getValue().indexOf(':');
|
|
||||||
if (colonIdx < 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String resType = nextInclude.getValue().substring(0, colonIdx);
|
|
||||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(resType);
|
|
||||||
if (def == null) {
|
|
||||||
ourLog.warn("Unknown resource type in _revinclude=" + nextInclude.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String paramName = nextInclude.getValue().substring(colonIdx + 1);
|
|
||||||
RuntimeSearchParam param = def.getSearchParam(paramName);
|
|
||||||
if (param == null) {
|
|
||||||
ourLog.warn("Unknown param name in _revinclude=" + nextInclude.getValue());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String nextPath : param.getPathsSplit()) {
|
|
||||||
String sql = "SELECT r FROM ResourceLink r WHERE r.mySourcePath = :src_path AND r.myTargetResourcePid IN (:target_pids)";
|
|
||||||
TypedQuery<ResourceLink> q = myEntityManager.createQuery(sql, ResourceLink.class);
|
|
||||||
q.setParameter("src_path", nextPath);
|
|
||||||
q.setParameter("target_pids", theMatches);
|
|
||||||
List<ResourceLink> results = q.getResultList();
|
|
||||||
for (ResourceLink resourceLink : results) {
|
|
||||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
theMatches.addAll(pidsToInclude);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider search(String theParameterName, IQueryParameterType theValue) {
|
public IBundleProvider search(String theParameterName, IQueryParameterType theValue) {
|
||||||
return search(Collections.singletonMap(theParameterName, theValue));
|
return search(Collections.singletonMap(theParameterName, theValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract List<Object> getIncludeValues(FhirTerser theTerser, Include theInclude, IResource theResource, RuntimeResourceDefinition theResourceDef);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Long> searchForIds(Map<String, IQueryParameterType> theParams) {
|
public Set<Long> searchForIds(Map<String, IQueryParameterType> theParams) {
|
||||||
SearchParameterMap map = new SearchParameterMap();
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
@ -1708,6 +1818,22 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
return qp;
|
return qp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ArrayList<TagDefinition> toTagList(MetaDt theMeta) {
|
||||||
|
ArrayList<TagDefinition> retVal = new ArrayList<TagDefinition>();
|
||||||
|
|
||||||
|
for (CodingDt next : theMeta.getTag()) {
|
||||||
|
retVal.add(new TagDefinition(TagTypeEnum.TAG, next.getSystem(), next.getCode(), next.getDisplay()));
|
||||||
|
}
|
||||||
|
for (CodingDt next : theMeta.getSecurity()) {
|
||||||
|
retVal.add(new TagDefinition(TagTypeEnum.SECURITY_LABEL, next.getSystem(), next.getCode(), next.getDisplay()));
|
||||||
|
}
|
||||||
|
for (UriDt next : theMeta.getProfile()) {
|
||||||
|
retVal.add(new TagDefinition(TagTypeEnum.PROFILE, BaseFhirDao.NS_JPA_PROFILE, next.getValue(), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DaoMethodOutcome update(T theResource) {
|
public DaoMethodOutcome update(T theResource) {
|
||||||
return update(theResource, null);
|
return update(theResource, null);
|
||||||
|
@ -1788,121 +1914,4 @@ public abstract class BaseFhirResourceDao<T extends IResource> extends BaseFhirD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MetaDt metaGetOperation() {
|
|
||||||
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type)";
|
|
||||||
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
|
|
||||||
q.setParameter("res_type", myResourceName);
|
|
||||||
List<TagDefinition> tagDefinitions = q.getResultList();
|
|
||||||
|
|
||||||
MetaDt retVal = super.toMetaDt(tagDefinitions);
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MetaDt metaGetOperation(IdDt theId) {
|
|
||||||
Long pid = super.translateForcedIdToPid(theId);
|
|
||||||
|
|
||||||
String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t WHERE t.myResourceType = :res_type AND t.myResourceId = :res_id)";
|
|
||||||
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
|
|
||||||
q.setParameter("res_type", myResourceName);
|
|
||||||
q.setParameter("res_id", pid);
|
|
||||||
List<TagDefinition> tagDefinitions = q.getResultList();
|
|
||||||
|
|
||||||
MetaDt retVal = super.toMetaDt(tagDefinitions);
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MetaDt metaDeleteOperation(IdDt theResourceId, MetaDt theMetaDel) {
|
|
||||||
StopWatch w = new StopWatch();
|
|
||||||
BaseHasResource entity = readEntity(theResourceId);
|
|
||||||
if (entity == null) {
|
|
||||||
throw new ResourceNotFoundException(theResourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TagDefinition> tags = toTagList(theMetaDel);
|
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
for (TagDefinition nextDef : tags) {
|
|
||||||
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
|
|
||||||
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
|
|
||||||
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
|
|
||||||
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
|
|
||||||
myEntityManager.remove(next);
|
|
||||||
entity.getTags().remove(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
if (entity.getTags().isEmpty()) {
|
|
||||||
entity.setHasTags(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
myEntityManager.merge(entity);
|
|
||||||
|
|
||||||
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
|
|
||||||
|
|
||||||
return metaGetOperation(theResourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MetaDt metaAddOperation(IdDt theResourceId, MetaDt theMetaAdd) {
|
|
||||||
StopWatch w = new StopWatch();
|
|
||||||
BaseHasResource entity = readEntity(theResourceId);
|
|
||||||
if (entity == null) {
|
|
||||||
throw new ResourceNotFoundException(theResourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TagDefinition> tags = toTagList(theMetaAdd);
|
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
for (TagDefinition nextDef : tags) {
|
|
||||||
|
|
||||||
boolean hasTag = false;
|
|
||||||
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
|
|
||||||
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
|
|
||||||
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
|
|
||||||
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
|
|
||||||
hasTag = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasTag) {
|
|
||||||
entity.setHasTags(true);
|
|
||||||
|
|
||||||
TagDefinition def = getTag(nextDef.getTagType(), nextDef.getSystem(), nextDef.getCode(), nextDef.getDisplay());
|
|
||||||
BaseTag newEntity = entity.addTag(def);
|
|
||||||
myEntityManager.persist(newEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
myEntityManager.merge(entity);
|
|
||||||
notifyWriteCompleted();
|
|
||||||
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[] { theResourceId, w.getMillisAndRestart() });
|
|
||||||
|
|
||||||
return metaGetOperation(theResourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArrayList<TagDefinition> toTagList(MetaDt theMeta) {
|
|
||||||
ArrayList<TagDefinition> retVal = new ArrayList<TagDefinition>();
|
|
||||||
|
|
||||||
for (CodingDt next : theMeta.getTag()) {
|
|
||||||
retVal.add(new TagDefinition(TagTypeEnum.TAG, next.getSystem(), next.getCode(), next.getDisplay()));
|
|
||||||
}
|
|
||||||
for (CodingDt next : theMeta.getSecurity()) {
|
|
||||||
retVal.add(new TagDefinition(TagTypeEnum.SECURITY_LABEL, next.getSystem(), next.getCode(), next.getDisplay()));
|
|
||||||
}
|
|
||||||
for (UriDt next : theMeta.getProfile()) {
|
|
||||||
retVal.add(new TagDefinition(TagTypeEnum.PROFILE, BaseFhirDao.NS_JPA_PROFILE, next.getValue(), null));
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
@ -50,6 +51,7 @@ import ca.uhn.fhir.model.dstu.resource.Practitioner;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
|
import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Condition;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
|
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.DocumentManifest;
|
import ca.uhn.fhir.model.dstu2.resource.DocumentManifest;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.DocumentReference;
|
import ca.uhn.fhir.model.dstu2.resource.DocumentReference;
|
||||||
|
@ -62,7 +64,9 @@ import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.EncounterClassEnum;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.EncounterStateEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
|
@ -277,11 +281,10 @@ public class ResourceProviderDstu2Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithInclude() throws Exception {
|
public void testSearchWithInclude() throws Exception {
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.addIdentifier().setSystem("urn:system:rpdstu2").setValue( "testSearchWithInclude01");
|
org.addIdentifier().setSystem("urn:system:rpdstu2").setValue("testSearchWithInclude01");
|
||||||
IdDt orgId = ourClient.create().resource(org).prettyPrint().encodedXml().execute().getId();
|
IdDt orgId = ourClient.create().resource(org).prettyPrint().encodedXml().execute().getId();
|
||||||
|
|
||||||
Patient pat = new Patient();
|
Patient pat = new Patient();
|
||||||
|
@ -347,7 +350,6 @@ public class ResourceProviderDstu2Test {
|
||||||
ids.add(next.getResource().getId());
|
ids.add(next.getResource().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
assertThat(ids, containsInAnyOrder(patientId, devId, obsId, encId, orgId1, orgId2));
|
assertThat(ids, containsInAnyOrder(patientId, devId, obsId, encId, orgId1, orgId2));
|
||||||
|
|
||||||
// _revinclude's are counted but not _include's
|
// _revinclude's are counted but not _include's
|
||||||
|
@ -360,10 +362,80 @@ public class ResourceProviderDstu2Test {
|
||||||
* See #147
|
* See #147
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testEverythingDoesnRepeatPatient() throws Exception {
|
public void testEverythingDoesntRepeatPatient() throws Exception {
|
||||||
ca.uhn.fhir.model.dstu2.resource.Bundle b;
|
ca.uhn.fhir.model.dstu2.resource.Bundle b;
|
||||||
b = ourFhirCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, new InputStreamReader(ResourceProviderDstu2Test.class.getResourceAsStream("/bug147-bundle.json")));
|
b = ourFhirCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, new InputStreamReader(ResourceProviderDstu2Test.class.getResourceAsStream("/bug147-bundle.json")));
|
||||||
|
|
||||||
|
ca.uhn.fhir.model.dstu2.resource.Bundle resp = ourClient.transaction().withBundle(b).execute();
|
||||||
|
List<IdDt> ids = new ArrayList<IdDt>();
|
||||||
|
for (Entry next : resp.getEntry()) {
|
||||||
|
IdDt toAdd = new IdDt(next.getTransactionResponse().getLocation()).toUnqualifiedVersionless();
|
||||||
|
ids.add(toAdd);
|
||||||
|
}
|
||||||
|
ourLog.info("Created: " + ids.toString());
|
||||||
|
|
||||||
|
IdDt patientId = new IdDt(resp.getEntry().get(1).getTransactionResponse().getLocation());
|
||||||
|
assertEquals("Patient", patientId.getResourceType());
|
||||||
|
|
||||||
|
{
|
||||||
|
Parameters output = ourClient.operation().onInstance(patientId).named("everything").withNoParameters(Parameters.class).execute();
|
||||||
|
b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
|
||||||
|
|
||||||
|
ids = new ArrayList<IdDt>();
|
||||||
|
boolean dupes = false;
|
||||||
|
for (Entry next : b.getEntry()) {
|
||||||
|
IdDt toAdd = next.getResource().getId().toUnqualifiedVersionless();
|
||||||
|
dupes = dupes | ids.contains(toAdd);
|
||||||
|
ids.add(toAdd);
|
||||||
|
}
|
||||||
|
ourLog.info("$everything: " + ids.toString());
|
||||||
|
|
||||||
|
assertFalse(ids.toString(), dupes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Condition/11 is the 11th resource and the default page size is 10 so we don't show the 11th.
|
||||||
|
*/
|
||||||
|
assertThat(ids.toString(), not(containsString("Condition")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now try with a size specified
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
Parameters input = new Parameters();
|
||||||
|
input.addParameter().setName(Constants.PARAM_COUNT).setValue(new IntegerDt(100));
|
||||||
|
Parameters output = ourClient.operation().onInstance(patientId).named("everything").withParameters(input).execute();
|
||||||
|
b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
|
||||||
|
|
||||||
|
ids = new ArrayList<IdDt>();
|
||||||
|
boolean dupes = false;
|
||||||
|
for (Entry next : b.getEntry()) {
|
||||||
|
IdDt toAdd = next.getResource().getId().toUnqualifiedVersionless();
|
||||||
|
dupes = dupes | ids.contains(toAdd);
|
||||||
|
ids.add(toAdd);
|
||||||
|
}
|
||||||
|
ourLog.info("$everything: " + ids.toString());
|
||||||
|
|
||||||
|
assertFalse(ids.toString(), dupes);
|
||||||
|
assertThat(ids.toString(), containsString("Condition"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #148
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEverythingIncludesCondition() throws Exception {
|
||||||
|
ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle();
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setId("1");
|
||||||
|
b.addEntry().setResource(p).getTransaction().setMethod(HTTPVerbEnum.POST);
|
||||||
|
|
||||||
|
Condition c = new Condition();
|
||||||
|
c.getPatient().setReference("Patient/1");
|
||||||
|
b.addEntry().setResource(c).getTransaction().setMethod(HTTPVerbEnum.POST);
|
||||||
|
|
||||||
ca.uhn.fhir.model.dstu2.resource.Bundle resp = ourClient.transaction().withBundle(b).execute();
|
ca.uhn.fhir.model.dstu2.resource.Bundle resp = ourClient.transaction().withBundle(b).execute();
|
||||||
|
|
||||||
ourLog.info(ourFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
|
ourLog.info(ourFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||||
|
@ -375,16 +447,14 @@ public class ResourceProviderDstu2Test {
|
||||||
b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
|
b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource();
|
||||||
|
|
||||||
List<IdDt> ids = new ArrayList<IdDt>();
|
List<IdDt> ids = new ArrayList<IdDt>();
|
||||||
boolean dupes = false;
|
|
||||||
for (Entry next : b.getEntry()) {
|
for (Entry next : b.getEntry()) {
|
||||||
IdDt toAdd = next.getResource().getId().toUnqualifiedVersionless();
|
IdDt toAdd = next.getResource().getId().toUnqualifiedVersionless();
|
||||||
dupes = dupes | ids.contains(toAdd);
|
|
||||||
ids.add(toAdd);
|
ids.add(toAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info(ids.toString());
|
assertThat(ids.toString(), containsString("Patient/"));
|
||||||
|
assertThat(ids.toString(), containsString("Condition/"));
|
||||||
|
|
||||||
assertFalse(ids.toString(), dupes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -477,7 +547,7 @@ public class ResourceProviderDstu2Test {
|
||||||
int initialSize = client.search().forResource(DiagnosticOrder.class).execute().size();
|
int initialSize = client.search().forResource(DiagnosticOrder.class).execute().size();
|
||||||
|
|
||||||
DiagnosticOrder res = new DiagnosticOrder();
|
DiagnosticOrder res = new DiagnosticOrder();
|
||||||
res.addIdentifier().setSystem("urn:foo").setValue( "123");
|
res.addIdentifier().setSystem("urn:foo").setValue("123");
|
||||||
|
|
||||||
client.create().resource(res).execute();
|
client.create().resource(res).execute();
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,11 @@
|
||||||
"entry":
|
"entry":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "Patient",
|
"resourceType": "Patient",
|
||||||
|
@ -114,7 +118,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "Encounter",
|
"resourceType": "Encounter",
|
||||||
|
@ -148,7 +156,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "ClinicalImpression",
|
"resourceType": "ClinicalImpression",
|
||||||
|
@ -193,7 +205,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "ClinicalImpression",
|
"resourceType": "ClinicalImpression",
|
||||||
|
@ -238,7 +254,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "Encounter",
|
"resourceType": "Encounter",
|
||||||
|
@ -267,7 +287,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "ClinicalImpression",
|
"resourceType": "ClinicalImpression",
|
||||||
|
@ -311,7 +335,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "Encounter",
|
"resourceType": "Encounter",
|
||||||
|
@ -340,7 +368,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "ClinicalImpression",
|
"resourceType": "ClinicalImpression",
|
||||||
|
@ -384,7 +416,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "Observation",
|
"resourceType": "Observation",
|
||||||
|
@ -417,7 +453,11 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"transaction": { "method":"POST" },
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
"resource":
|
"resource":
|
||||||
{
|
{
|
||||||
"resourceType": "Observation",
|
"resourceType": "Observation",
|
||||||
|
@ -447,7 +487,50 @@
|
||||||
{
|
{
|
||||||
"mode": "match"
|
"mode": "match"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"transaction":
|
||||||
|
{
|
||||||
|
"method": "POST"
|
||||||
|
},
|
||||||
|
|
||||||
|
"resource":
|
||||||
|
{
|
||||||
|
"resourceType": "Condition",
|
||||||
|
"id": "20443",
|
||||||
|
"meta":
|
||||||
|
{
|
||||||
|
"versionId": "1",
|
||||||
|
"lastUpdated": "2015-04-03T22:56:49.886-04:00"
|
||||||
|
},
|
||||||
|
|
||||||
|
"text":
|
||||||
|
{
|
||||||
|
"status": "generated",
|
||||||
|
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">pharyngitis working </div>"
|
||||||
|
},
|
||||||
|
|
||||||
|
"patient":
|
||||||
|
{
|
||||||
|
"reference": "Patient/1702",
|
||||||
|
"display": "Eve Everywoman "
|
||||||
|
},
|
||||||
|
|
||||||
|
"code":
|
||||||
|
{
|
||||||
|
"coding":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"system": "http://loinc.org",
|
||||||
|
"code": "28397-8",
|
||||||
|
"display": "pharyngitis"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"text": "pharyngitis"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -35,7 +35,7 @@
|
||||||
<property name="persistenceUnitName" value="FHIR_UT" />
|
<property name="persistenceUnitName" value="FHIR_UT" />
|
||||||
<property name="jpaVendorAdapter">
|
<property name="jpaVendorAdapter">
|
||||||
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
|
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
|
||||||
<property name="showSql" value="true" />
|
<property name="showSql" value="false" />
|
||||||
<property name="generateDdl" value="true" />
|
<property name="generateDdl" value="true" />
|
||||||
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
|
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
|
||||||
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
||||||
|
|
|
@ -268,7 +268,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseResource getResourceBundle() {
|
public IResource getResourceBundle() {
|
||||||
return myBundle;
|
return myBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,9 @@
|
||||||
the main focus resource if it was referred to in a deep chain. Thanks
|
the main focus resource if it was referred to in a deep chain. Thanks
|
||||||
to David Hay for reporting!
|
to David Hay for reporting!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add" issue="148">
|
||||||
|
JPA Server $everything operation now allows a _count parameter
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.9" date="2015-Mar-14">
|
<release version="0.9" date="2015-Mar-14">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue