7.6.0 Mergeback (#6494)
* 6323 resource creation deadlock (#6324) * make map reads concurrent * change log * CUstom version number for guaranteed build determinism * licenses * Expand translation cache (#6341) * Expand translation cache * Add changelog * Correction to #6341 (#6342) * Contained bug (#6402) * Contained bug * more tests * changelog, tests, implementation * code review * backwards logic * Fix Questionnaire doc (#6400) * fix reindex optimizeStorage=ALL_VERSIONS (#6421) * fixing broken rename of last step of reindex (#6429) * Fixes for the translation of parameter issues as part of the output for $validate-code operation. (#6438) * Move the validation providers to the test utilities package such that they can be reused. * Update IValidationSupport.CodeValidationIssue structure such that it meets the FHIR specification. Update RemoteTerminologyServiceValidationSupport and VersionSpecificWorkerContextWrapper translation for issues. * New tests for issue translation. Move test class to a different package so that we can add another test class. * Some simplification in the issue API to simplify building of issues. * Fix compilation errors. * Update providers to allow multiple responses and add support for the fetchCodeSystem call through method find. * Setup the first test for resource validation with remote terminology providers. * Fix NullPointerException * avoid calling validateCode for CodeSystem where system is null * Keep old public API methods (and class name) in IValidationSupport and mark them as deprecated to avoid breaking dependencies. * Revert local change to debug for duplicate errors. * Add more test cases for resource validation. Throw exception to signal missing test setup to make it obvious. * Simplify test setup. * Add some more javadoc * Add javadoc for the new test class * Add more tests * Address code review comments in IValidationSupport. * Add changelog * Change Repository search interface from Map to Multimap (#6445) Change Map to Multimap to support multiple and clauses. * licenses * fix interceptor hooks from requestDetails not getting called for STORAGE_PRECHECK_FOR_CACHED_SEARCH (#6436) * fix interceptor hooks from requestDetails not getting called for STORAGE_PRECHECK_FOR_CACHED_SEARCH * added unit tests and updated changelog * added one more test case * Add Adapter api (#6450) Lightweight implementation of the adapter pattern. * Bulk Import job status not changed after activation - failing test, fix, changelog (#6452) * Update CR to 3.13.1 (#6433) * Automated Migration Testing (HAPI-FHIR) - updated test migration scripts for 7_4_0 (#6439) * Rel 7 6 CVE (#6446) * Bump commons io * Bump HS and Lucene, and hibernate * Jetty bump for CVE * Resolve CVES * Bump with mismatch lucene * Bump velocity template engine * Revert bom bump * wip * Version bump * move changelog, fix cve * Bump commons-lang * Replace imports * wip * fix HQL break * remove dead code * Fix changelog entry * Bump org.hl7.fhir.core to 6.4.0 (#6454) * Bump HAPI version + org.hl7.fhir.core to 6.4.0 * Apply spotless * Correct a bug with duplicate parser IDs being assigned. (#6456) * Fix changelog entry * wip * compilation problem * Correct tests * changelog * Update hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/JsonParserR4Test.java Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> * Correct a bug with duplicate parser IDs being assigned - test fixes * Correct a bug with duplicate parser IDs being assigned - spotless * Correct a bug with duplicate parser IDs being assigned - added test-utilities dependency to fhir-structures poms * Correct a bug with duplicate parser IDs being assigned - test fixes * Contained resources without assigned IDs are now assigned GUIDs - address comments --------- Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: volodymyr <volodymyr.korzh@smilecdr.com> * licenses * ValueSet expansion fails if Hibernate Search configured to use Lucene - fixed incompatibility between Hibernate Search and Lucene versions (#6468) * Change the migrator to avoid table locks when adding an index. (#6489) * version bump * Updating version to: 7.6.1 post release. * Bump to 7.7.7. * deadsapce * Profile reference param (#6501) * updated QueryStack to not throw error with _profile as a ReferenceParam * update QueryStack * remove warnings * cleanup cast * cleanup test * add changelog * cleanup imports * updates per review feedback * updates per review feedback --------- Co-authored-by: taha.attari@smilecdr.com <taha.attari@smilecdr.com> * move changelog --------- Co-authored-by: JasonRoberts-smile <85363818+JasonRoberts-smile@users.noreply.github.com> Co-authored-by: James Agnew <jamesagnew@gmail.com> Co-authored-by: Brenin Rhodes <brenin@alphora.com> Co-authored-by: Emre Dincturk <74370953+mrdnctrk@users.noreply.github.com> Co-authored-by: TipzCM <leif.stawnyczy@gmail.com> Co-authored-by: Martha Mitran <martha.mitran@smiledigitalhealth.com> Co-authored-by: Michael Buckley <michaelabuckley@gmail.com> Co-authored-by: volodymyr-korzh <132366313+volodymyr-korzh@users.noreply.github.com> Co-authored-by: dotasek <david.otasek@smilecdr.com> Co-authored-by: volodymyr <volodymyr.korzh@smilecdr.com> Co-authored-by: markiantorno <markiantorno@gmail.com> Co-authored-by: Gary Graham <garygraham@smiledigitalhealth.com> Co-authored-by: Taha <TahaAttari@users.noreply.github.com> Co-authored-by: taha.attari@smilecdr.com <taha.attari@smilecdr.com>
This commit is contained in:
parent
d4f1766ca5
commit
86c2c13e0f
|
@ -440,74 +440,259 @@ public interface IValidationSupport {
|
|||
return "Unknown " + getFhirContext().getVersion().getVersion() + " Validation Support";
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines codes in system <a href="http://hl7.org/fhir/issue-severity">http://hl7.org/fhir/issue-severity</a>.
|
||||
*/
|
||||
/* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */
|
||||
enum IssueSeverity {
|
||||
/**
|
||||
* The issue caused the action to fail, and no further checking could be performed.
|
||||
*/
|
||||
FATAL,
|
||||
FATAL("fatal"),
|
||||
/**
|
||||
* The issue is sufficiently important to cause the action to fail.
|
||||
*/
|
||||
ERROR,
|
||||
ERROR("error"),
|
||||
/**
|
||||
* The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.
|
||||
*/
|
||||
WARNING,
|
||||
WARNING("warning"),
|
||||
/**
|
||||
* The issue has no relation to the degree of success of the action.
|
||||
*/
|
||||
INFORMATION
|
||||
INFORMATION("information"),
|
||||
/**
|
||||
* The operation was successful.
|
||||
*/
|
||||
SUCCESS("success");
|
||||
// the spec for OperationOutcome mentions that a code from http://hl7.org/fhir/issue-severity is required
|
||||
|
||||
private final String myCode;
|
||||
|
||||
IssueSeverity(String theCode) {
|
||||
myCode = theCode;
|
||||
}
|
||||
/**
|
||||
* Provide mapping to a code in system <a href="http://hl7.org/fhir/issue-severity">http://hl7.org/fhir/issue-severity</a>.
|
||||
* @return the code
|
||||
*/
|
||||
public String getCode() {
|
||||
return myCode;
|
||||
}
|
||||
/**
|
||||
* Creates a {@link IssueSeverity} object from the given code.
|
||||
* @return the {@link IssueSeverity}
|
||||
*/
|
||||
public static IssueSeverity fromCode(String theCode) {
|
||||
switch (theCode) {
|
||||
case "fatal":
|
||||
return FATAL;
|
||||
case "error":
|
||||
return ERROR;
|
||||
case "warning":
|
||||
return WARNING;
|
||||
case "information":
|
||||
return INFORMATION;
|
||||
case "success":
|
||||
return SUCCESS;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CodeValidationIssueCode {
|
||||
NOT_FOUND,
|
||||
CODE_INVALID,
|
||||
INVALID,
|
||||
OTHER
|
||||
/**
|
||||
* Defines codes in system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>.
|
||||
* The binding is enforced as a part of validation logic in the FHIR Core Validation library where an exception is thrown.
|
||||
* Only a sub-set of these codes are defined as constants because they relate to validation,
|
||||
* If there are additional ones that come up, for Remote Terminology they are currently supported via
|
||||
* {@link IValidationSupport.CodeValidationIssue#CodeValidationIssue(String, IssueSeverity, String)}
|
||||
* while for internal validators, more constants can be added to make things easier and consistent.
|
||||
* This maps to resource OperationOutcome.issue.code.
|
||||
*/
|
||||
/* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */
|
||||
class CodeValidationIssueCode {
|
||||
public static final CodeValidationIssueCode NOT_FOUND = new CodeValidationIssueCode("not-found");
|
||||
public static final CodeValidationIssueCode CODE_INVALID = new CodeValidationIssueCode("code-invalid");
|
||||
public static final CodeValidationIssueCode INVALID = new CodeValidationIssueCode("invalid");
|
||||
|
||||
private final String myCode;
|
||||
|
||||
// this is intentionally not exposed
|
||||
CodeValidationIssueCode(String theCode) {
|
||||
myCode = theCode;
|
||||
}
|
||||
|
||||
enum CodeValidationIssueCoding {
|
||||
VS_INVALID,
|
||||
NOT_FOUND,
|
||||
NOT_IN_VS,
|
||||
|
||||
INVALID_CODE,
|
||||
INVALID_DISPLAY,
|
||||
OTHER
|
||||
/**
|
||||
* Retrieve the corresponding code from system <a href="http://hl7.org/fhir/issue-type">http://hl7.org/fhir/issue-type</a>.
|
||||
* @return the code
|
||||
*/
|
||||
public String getCode() {
|
||||
return myCode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds information about the details of a {@link CodeValidationIssue}.
|
||||
* This maps to resource OperationOutcome.issue.details.
|
||||
*/
|
||||
/* this enum would not be needed if we design/refactor to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult */
|
||||
class CodeValidationIssueDetails {
|
||||
private final String myText;
|
||||
private List<CodeValidationIssueCoding> myCodings;
|
||||
|
||||
public CodeValidationIssueDetails(String theText) {
|
||||
myText = theText;
|
||||
}
|
||||
|
||||
// intentionally not exposed
|
||||
void addCoding(CodeValidationIssueCoding theCoding) {
|
||||
getCodings().add(theCoding);
|
||||
}
|
||||
|
||||
public CodeValidationIssueDetails addCoding(String theSystem, String theCode) {
|
||||
if (myCodings == null) {
|
||||
myCodings = new ArrayList<>();
|
||||
}
|
||||
myCodings.add(new CodeValidationIssueCoding(theSystem, theCode));
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return myText;
|
||||
}
|
||||
|
||||
public List<CodeValidationIssueCoding> getCodings() {
|
||||
if (myCodings == null) {
|
||||
myCodings = new ArrayList<>();
|
||||
}
|
||||
return myCodings;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines codes that can be part of the details of an issue.
|
||||
* There are some constants available (pre-defined) for codes for system <a href="http://hl7.org/fhir/tools/CodeSystem/tx-issue-type">http://hl7.org/fhir/tools/CodeSystem/tx-issue-type</a>.
|
||||
* This maps to resource OperationOutcome.issue.details.coding[0].code.
|
||||
*/
|
||||
class CodeValidationIssueCoding {
|
||||
public static String TX_ISSUE_SYSTEM = "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type";
|
||||
public static CodeValidationIssueCoding VS_INVALID =
|
||||
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "vs-invalid");
|
||||
public static final CodeValidationIssueCoding NOT_FOUND =
|
||||
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "not-found");
|
||||
public static final CodeValidationIssueCoding NOT_IN_VS =
|
||||
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "not-in-vs");
|
||||
public static final CodeValidationIssueCoding INVALID_CODE =
|
||||
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "invalid-code");
|
||||
public static final CodeValidationIssueCoding INVALID_DISPLAY =
|
||||
new CodeValidationIssueCoding(TX_ISSUE_SYSTEM, "vs-display");
|
||||
private final String mySystem, myCode;
|
||||
|
||||
// this is intentionally not exposed
|
||||
CodeValidationIssueCoding(String theSystem, String theCode) {
|
||||
mySystem = theSystem;
|
||||
myCode = theCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the corresponding code for the details of a validation issue.
|
||||
* @return the code
|
||||
*/
|
||||
public String getCode() {
|
||||
return myCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the system for the details of a validation issue.
|
||||
* @return the system
|
||||
*/
|
||||
public String getSystem() {
|
||||
return mySystem;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a hapi-fhir internal version agnostic object holding information about a validation issue.
|
||||
* An alternative (which requires significant refactoring) would be to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult instead.
|
||||
*/
|
||||
class CodeValidationIssue {
|
||||
|
||||
private final String myMessage;
|
||||
private final String myDiagnostics;
|
||||
private final IssueSeverity mySeverity;
|
||||
private final CodeValidationIssueCode myCode;
|
||||
private final CodeValidationIssueCoding myCoding;
|
||||
private CodeValidationIssueDetails myDetails;
|
||||
|
||||
public CodeValidationIssue(
|
||||
String theMessage,
|
||||
IssueSeverity mySeverity,
|
||||
CodeValidationIssueCode theCode,
|
||||
CodeValidationIssueCoding theCoding) {
|
||||
this.myMessage = theMessage;
|
||||
this.mySeverity = mySeverity;
|
||||
this.myCode = theCode;
|
||||
this.myCoding = theCoding;
|
||||
String theDiagnostics, IssueSeverity theSeverity, CodeValidationIssueCode theTypeCode) {
|
||||
this(theDiagnostics, theSeverity, theTypeCode, null);
|
||||
}
|
||||
|
||||
public CodeValidationIssue(String theDiagnostics, IssueSeverity theSeverity, String theTypeCode) {
|
||||
this(theDiagnostics, theSeverity, new CodeValidationIssueCode(theTypeCode), null);
|
||||
}
|
||||
|
||||
public CodeValidationIssue(
|
||||
String theDiagnostics,
|
||||
IssueSeverity theSeverity,
|
||||
CodeValidationIssueCode theType,
|
||||
CodeValidationIssueCoding theDetailsCoding) {
|
||||
myDiagnostics = theDiagnostics;
|
||||
mySeverity = theSeverity;
|
||||
myCode = theType;
|
||||
// reuse the diagnostics message as a detail text message
|
||||
myDetails = new CodeValidationIssueDetails(theDiagnostics);
|
||||
myDetails.addCoding(theDetailsCoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use {@link #getDiagnostics()} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public String getMessage() {
|
||||
return myMessage;
|
||||
return getDiagnostics();
|
||||
}
|
||||
|
||||
public String getDiagnostics() {
|
||||
return myDiagnostics;
|
||||
}
|
||||
|
||||
public IssueSeverity getSeverity() {
|
||||
return mySeverity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use {@link #getType()} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationIssueCode getCode() {
|
||||
return getType();
|
||||
}
|
||||
|
||||
public CodeValidationIssueCode getType() {
|
||||
return myCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use {@link #getDetails()} instead. That has support for multiple codings.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationIssueCoding getCoding() {
|
||||
return myCoding;
|
||||
return myDetails != null
|
||||
? myDetails.getCodings().stream().findFirst().orElse(null)
|
||||
: null;
|
||||
}
|
||||
|
||||
public void setDetails(CodeValidationIssueDetails theDetails) {
|
||||
this.myDetails = theDetails;
|
||||
}
|
||||
|
||||
public CodeValidationIssueDetails getDetails() {
|
||||
return myDetails;
|
||||
}
|
||||
|
||||
public boolean hasIssueDetailCode(@Nonnull String theCode) {
|
||||
// this method is system agnostic at the moment but it can be restricted if needed
|
||||
return myDetails.getCodings().stream().anyMatch(coding -> theCode.equals(coding.getCode()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -671,6 +856,10 @@ public interface IValidationSupport {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a hapi-fhir internal version agnostic object holding information about the validation result.
|
||||
* An alternative (which requires significant refactoring) would be to use org.hl7.fhir.r5.terminologies.utilities.ValidationResult.
|
||||
*/
|
||||
class CodeValidationResult {
|
||||
public static final String SOURCE_DETAILS = "sourceDetails";
|
||||
public static final String RESULT = "result";
|
||||
|
@ -686,7 +875,7 @@ public interface IValidationSupport {
|
|||
private String myDisplay;
|
||||
private String mySourceDetails;
|
||||
|
||||
private List<CodeValidationIssue> myCodeValidationIssues;
|
||||
private List<CodeValidationIssue> myIssues;
|
||||
|
||||
public CodeValidationResult() {
|
||||
super();
|
||||
|
@ -771,20 +960,45 @@ public interface IValidationSupport {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use method {@link #getIssues()} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public List<CodeValidationIssue> getCodeValidationIssues() {
|
||||
if (myCodeValidationIssues == null) {
|
||||
myCodeValidationIssues = new ArrayList<>();
|
||||
}
|
||||
return myCodeValidationIssues;
|
||||
return getIssues();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use method {@link #setIssues(List)} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationResult setCodeValidationIssues(List<CodeValidationIssue> theCodeValidationIssues) {
|
||||
myCodeValidationIssues = new ArrayList<>(theCodeValidationIssues);
|
||||
return setIssues(theCodeValidationIssues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Please use method {@link #addIssue(CodeValidationIssue)} instead.
|
||||
*/
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) {
|
||||
getCodeValidationIssues().add(theCodeValidationIssue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeValidationResult addCodeValidationIssue(CodeValidationIssue theCodeValidationIssue) {
|
||||
getCodeValidationIssues().add(theCodeValidationIssue);
|
||||
public List<CodeValidationIssue> getIssues() {
|
||||
if (myIssues == null) {
|
||||
myIssues = new ArrayList<>();
|
||||
}
|
||||
return myIssues;
|
||||
}
|
||||
|
||||
public CodeValidationResult setIssues(List<CodeValidationIssue> theIssues) {
|
||||
myIssues = new ArrayList<>(theIssues);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CodeValidationResult addIssue(CodeValidationIssue theCodeValidationIssue) {
|
||||
getIssues().add(theCodeValidationIssue);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -811,17 +1025,19 @@ public interface IValidationSupport {
|
|||
public String getSeverityCode() {
|
||||
String retVal = null;
|
||||
if (getSeverity() != null) {
|
||||
retVal = getSeverity().name().toLowerCase();
|
||||
retVal = getSeverity().getCode();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an issue severity as a string code. Value must be the name of
|
||||
* one of the enum values in {@link IssueSeverity}. Value is case-insensitive.
|
||||
* Sets an issue severity using a severity code. Please use method {@link #setSeverity(IssueSeverity)} instead.
|
||||
* @param theSeverityCode the code
|
||||
* @return the current {@link CodeValidationResult} instance
|
||||
*/
|
||||
public CodeValidationResult setSeverityCode(@Nonnull String theIssueSeverity) {
|
||||
setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase()));
|
||||
@Deprecated(since = "7.4.6")
|
||||
public CodeValidationResult setSeverityCode(@Nonnull String theSeverityCode) {
|
||||
setSeverity(IssueSeverity.fromCode(theSeverityCode));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -838,6 +1054,11 @@ public interface IValidationSupport {
|
|||
if (isNotBlank(getSourceDetails())) {
|
||||
ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails());
|
||||
}
|
||||
/*
|
||||
should translate issues as well, except that is version specific code, so it requires more refactoring
|
||||
or replace the current class with org.hl7.fhir.r5.terminologies.utilities.ValidationResult
|
||||
@see VersionSpecificWorkerContextWrapper#getIssuesForCodeValidation
|
||||
*/
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -105,7 +105,6 @@ public abstract class BaseParser implements IParser {
|
|||
private static final Set<String> notEncodeForContainedResource =
|
||||
new HashSet<>(Arrays.asList("security", "versionId", "lastUpdated"));
|
||||
|
||||
private FhirTerser.ContainedResources myContainedResources;
|
||||
private boolean myEncodeElementsAppliesToChildResourcesOnly;
|
||||
private final FhirContext myContext;
|
||||
private Collection<String> myDontEncodeElements;
|
||||
|
@ -183,12 +182,15 @@ public abstract class BaseParser implements IParser {
|
|||
}
|
||||
|
||||
private String determineReferenceText(
|
||||
IBaseReference theRef, CompositeChildElement theCompositeChildElement, IBaseResource theResource) {
|
||||
IBaseReference theRef,
|
||||
CompositeChildElement theCompositeChildElement,
|
||||
IBaseResource theResource,
|
||||
EncodeContext theContext) {
|
||||
IIdType ref = theRef.getReferenceElement();
|
||||
if (isBlank(ref.getIdPart())) {
|
||||
String reference = ref.getValue();
|
||||
if (theRef.getResource() != null) {
|
||||
IIdType containedId = getContainedResources().getResourceId(theRef.getResource());
|
||||
IIdType containedId = theContext.getContainedResources().getResourceId(theRef.getResource());
|
||||
if (containedId != null && !containedId.isEmpty()) {
|
||||
if (containedId.isLocal()) {
|
||||
reference = containedId.getValue();
|
||||
|
@ -262,7 +264,8 @@ public abstract class BaseParser implements IParser {
|
|||
@Override
|
||||
public final void encodeResourceToWriter(IBaseResource theResource, Writer theWriter)
|
||||
throws IOException, DataFormatException {
|
||||
EncodeContext encodeContext = new EncodeContext(this, myContext.getParserOptions());
|
||||
EncodeContext encodeContext =
|
||||
new EncodeContext(this, myContext.getParserOptions(), new FhirTerser.ContainedResources());
|
||||
encodeResourceToWriter(theResource, theWriter, encodeContext);
|
||||
}
|
||||
|
||||
|
@ -285,7 +288,8 @@ public abstract class BaseParser implements IParser {
|
|||
} else if (theElement instanceof IPrimitiveType) {
|
||||
theWriter.write(((IPrimitiveType<?>) theElement).getValueAsString());
|
||||
} else {
|
||||
EncodeContext encodeContext = new EncodeContext(this, myContext.getParserOptions());
|
||||
EncodeContext encodeContext =
|
||||
new EncodeContext(this, myContext.getParserOptions(), new FhirTerser.ContainedResources());
|
||||
encodeToWriter(theElement, theWriter, encodeContext);
|
||||
}
|
||||
}
|
||||
|
@ -404,10 +408,6 @@ public abstract class BaseParser implements IParser {
|
|||
return elementId;
|
||||
}
|
||||
|
||||
FhirTerser.ContainedResources getContainedResources() {
|
||||
return myContainedResources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getDontStripVersionsFromReferencesAtPaths() {
|
||||
return myDontStripVersionsFromReferencesAtPaths;
|
||||
|
@ -539,10 +539,11 @@ public abstract class BaseParser implements IParser {
|
|||
return mySuppressNarratives;
|
||||
}
|
||||
|
||||
protected boolean isChildContained(BaseRuntimeElementDefinition<?> childDef, boolean theIncludedResource) {
|
||||
protected boolean isChildContained(
|
||||
BaseRuntimeElementDefinition<?> childDef, boolean theIncludedResource, EncodeContext theContext) {
|
||||
return (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES
|
||||
|| childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST)
|
||||
&& getContainedResources().isEmpty() == false
|
||||
&& theContext.getContainedResources().isEmpty() == false
|
||||
&& theIncludedResource == false;
|
||||
}
|
||||
|
||||
|
@ -788,7 +789,8 @@ public abstract class BaseParser implements IParser {
|
|||
*/
|
||||
if (next instanceof IBaseReference) {
|
||||
IBaseReference nextRef = (IBaseReference) next;
|
||||
String refText = determineReferenceText(nextRef, theCompositeChildElement, theResource);
|
||||
String refText =
|
||||
determineReferenceText(nextRef, theCompositeChildElement, theResource, theEncodeContext);
|
||||
if (!StringUtils.equals(refText, nextRef.getReferenceElement().getValue())) {
|
||||
|
||||
if (retVal == theValues) {
|
||||
|
@ -980,7 +982,7 @@ public abstract class BaseParser implements IParser {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected void containResourcesInReferences(IBaseResource theResource) {
|
||||
protected void containResourcesInReferences(IBaseResource theResource, EncodeContext theContext) {
|
||||
|
||||
/*
|
||||
* If a UUID is present in Bundle.entry.fullUrl but no value is present
|
||||
|
@ -1003,7 +1005,7 @@ public abstract class BaseParser implements IParser {
|
|||
}
|
||||
}
|
||||
|
||||
myContainedResources = getContext().newTerser().containResources(theResource);
|
||||
theContext.setContainedResources(getContext().newTerser().containResources(theResource));
|
||||
}
|
||||
|
||||
static class ChildNameAndDef {
|
||||
|
@ -1034,8 +1036,12 @@ public abstract class BaseParser implements IParser {
|
|||
private final List<EncodeContextPath> myEncodeElementPaths;
|
||||
private final Set<String> myEncodeElementsAppliesToResourceTypes;
|
||||
private final List<EncodeContextPath> myDontEncodeElementPaths;
|
||||
private FhirTerser.ContainedResources myContainedResources;
|
||||
|
||||
public EncodeContext(BaseParser theParser, ParserOptions theParserOptions) {
|
||||
public EncodeContext(
|
||||
BaseParser theParser,
|
||||
ParserOptions theParserOptions,
|
||||
FhirTerser.ContainedResources theContainedResources) {
|
||||
Collection<String> encodeElements = theParser.myEncodeElements;
|
||||
Collection<String> dontEncodeElements = theParser.myDontEncodeElements;
|
||||
if (isSummaryMode()) {
|
||||
|
@ -1058,6 +1064,8 @@ public abstract class BaseParser implements IParser {
|
|||
dontEncodeElements.stream().map(EncodeContextPath::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
myContainedResources = theContainedResources;
|
||||
|
||||
myEncodeElementsAppliesToResourceTypes =
|
||||
ParserUtil.determineApplicableResourceTypesForTerserPaths(myEncodeElementPaths);
|
||||
}
|
||||
|
@ -1065,6 +1073,14 @@ public abstract class BaseParser implements IParser {
|
|||
private Map<Key, List<BaseParser.CompositeChildElement>> getCompositeChildrenCache() {
|
||||
return myCompositeChildrenCache;
|
||||
}
|
||||
|
||||
public FhirTerser.ContainedResources getContainedResources() {
|
||||
return myContainedResources;
|
||||
}
|
||||
|
||||
public void setContainedResources(FhirTerser.ContainedResources theContainedResources) {
|
||||
myContainedResources = theContainedResources;
|
||||
}
|
||||
}
|
||||
|
||||
protected class CompositeChildElement {
|
||||
|
|
|
@ -54,6 +54,7 @@ import ca.uhn.fhir.parser.json.JsonLikeStructure;
|
|||
import ca.uhn.fhir.parser.json.jackson.JacksonStructure;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.util.ElementUtil;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.text.WordUtils;
|
||||
|
@ -386,12 +387,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
case CONTAINED_RESOURCE_LIST:
|
||||
case CONTAINED_RESOURCES: {
|
||||
List<IBaseResource> containedResources = getContainedResources().getContainedResources();
|
||||
List<IBaseResource> containedResources =
|
||||
theEncodeContext.getContainedResources().getContainedResources();
|
||||
if (containedResources.size() > 0) {
|
||||
beginArray(theEventWriter, theChildName);
|
||||
|
||||
for (IBaseResource next : containedResources) {
|
||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||
IIdType resourceId =
|
||||
theEncodeContext.getContainedResources().getResourceId(next);
|
||||
String value = resourceId.getValue();
|
||||
encodeResourceToJsonStreamWriter(
|
||||
theResDef,
|
||||
|
@ -554,7 +557,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
|
||||
if (nextValue == null || nextValue.isEmpty()) {
|
||||
if (nextValue instanceof BaseContainedDt) {
|
||||
if (theContainedResource || getContainedResources().isEmpty()) {
|
||||
if (theContainedResource
|
||||
|| theEncodeContext.getContainedResources().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
|
@ -838,7 +842,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
+ theResource.getStructureFhirVersionEnum());
|
||||
}
|
||||
|
||||
EncodeContext encodeContext = new EncodeContext(this, getContext().getParserOptions());
|
||||
EncodeContext encodeContext =
|
||||
new EncodeContext(this, getContext().getParserOptions(), new FhirTerser.ContainedResources());
|
||||
String resourceName = getContext().getResourceType(theResource);
|
||||
encodeContext.pushPath(resourceName, true);
|
||||
doEncodeResourceToJsonLikeWriter(theResource, theJsonLikeWriter, encodeContext);
|
||||
|
@ -894,7 +899,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
|
|||
}
|
||||
|
||||
if (!theContainedResource) {
|
||||
containResourcesInReferences(theResource);
|
||||
containResourcesInReferences(theResource, theEncodeContext);
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition resDef = getContext().getResourceDefinition(theResource);
|
||||
|
|
|
@ -191,7 +191,7 @@ public class RDFParser extends BaseParser {
|
|||
}
|
||||
|
||||
if (!containedResource) {
|
||||
containResourcesInReferences(resource);
|
||||
containResourcesInReferences(resource, encodeContext);
|
||||
}
|
||||
|
||||
if (!(resource instanceof IAnyResource)) {
|
||||
|
@ -354,7 +354,7 @@ public class RDFParser extends BaseParser {
|
|||
try {
|
||||
|
||||
if (element == null || element.isEmpty()) {
|
||||
if (!isChildContained(childDef, includedResource)) {
|
||||
if (!isChildContained(childDef, includedResource, theEncodeContext)) {
|
||||
return rdfModel;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -295,7 +295,7 @@ public class XmlParser extends BaseParser {
|
|||
try {
|
||||
|
||||
if (theElement == null || theElement.isEmpty()) {
|
||||
if (isChildContained(childDef, theIncludedResource)) {
|
||||
if (isChildContained(childDef, theIncludedResource, theEncodeContext)) {
|
||||
// We still want to go in..
|
||||
} else {
|
||||
return;
|
||||
|
@ -359,8 +359,10 @@ public class XmlParser extends BaseParser {
|
|||
* theEventWriter.writeStartElement("contained"); encodeResourceToXmlStreamWriter(next, theEventWriter, true, fixContainedResourceId(next.getId().getValue()));
|
||||
* theEventWriter.writeEndElement(); }
|
||||
*/
|
||||
for (IBaseResource next : getContainedResources().getContainedResources()) {
|
||||
IIdType resourceId = getContainedResources().getResourceId(next);
|
||||
for (IBaseResource next :
|
||||
theEncodeContext.getContainedResources().getContainedResources()) {
|
||||
IIdType resourceId =
|
||||
theEncodeContext.getContainedResources().getResourceId(next);
|
||||
theEventWriter.writeStartElement("contained");
|
||||
String value = resourceId.getValue();
|
||||
encodeResourceToXmlStreamWriter(
|
||||
|
@ -682,7 +684,7 @@ public class XmlParser extends BaseParser {
|
|||
}
|
||||
|
||||
if (!theContainedResource) {
|
||||
containResourcesInReferences(theResource);
|
||||
containResourcesInReferences(theResource, theEncodeContext);
|
||||
}
|
||||
|
||||
theEventWriter.writeStartElement(resDef.getName());
|
||||
|
|
|
@ -28,6 +28,8 @@ import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||
import com.google.common.annotations.Beta;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseConformance;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
|
@ -231,6 +233,23 @@ public interface Repository {
|
|||
|
||||
// Querying starts here
|
||||
|
||||
/**
|
||||
* Searches this repository
|
||||
*
|
||||
* @see <a href="https://www.hl7.org/fhir/http.html#search">FHIR search</a>
|
||||
*
|
||||
* @param <B> a Bundle type
|
||||
* @param <T> a Resource type
|
||||
* @param bundleType the class of the Bundle type to return
|
||||
* @param resourceType the class of the Resource type to search
|
||||
* @param searchParameters the searchParameters for this search
|
||||
* @return a Bundle with the results of the search
|
||||
*/
|
||||
default <B extends IBaseBundle, T extends IBaseResource> B search(
|
||||
Class<B> bundleType, Class<T> resourceType, Multimap<String, List<IQueryParameterType>> searchParameters) {
|
||||
return this.search(bundleType, resourceType, searchParameters, Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches this repository
|
||||
*
|
||||
|
@ -264,9 +283,32 @@ public interface Repository {
|
|||
<B extends IBaseBundle, T extends IBaseResource> B search(
|
||||
Class<B> bundleType,
|
||||
Class<T> resourceType,
|
||||
Map<String, List<IQueryParameterType>> searchParameters,
|
||||
Multimap<String, List<IQueryParameterType>> searchParameters,
|
||||
Map<String, String> headers);
|
||||
|
||||
/**
|
||||
* Searches this repository
|
||||
*
|
||||
* @see <a href="https://www.hl7.org/fhir/http.html#search">FHIR search</a>
|
||||
*
|
||||
* @param <B> a Bundle type
|
||||
* @param <T> a Resource type
|
||||
* @param bundleType the class of the Bundle type to return
|
||||
* @param resourceType the class of the Resource type to search
|
||||
* @param searchParameters the searchParameters for this search
|
||||
* @param headers headers for this request, typically key-value pairs of HTTP headers
|
||||
* @return a Bundle with the results of the search
|
||||
*/
|
||||
default <B extends IBaseBundle, T extends IBaseResource> B search(
|
||||
Class<B> bundleType,
|
||||
Class<T> resourceType,
|
||||
Map<String, List<IQueryParameterType>> searchParameters,
|
||||
Map<String, String> headers) {
|
||||
ArrayListMultimap<String, List<IQueryParameterType>> multimap = ArrayListMultimap.create();
|
||||
searchParameters.forEach(multimap::put);
|
||||
return this.search(bundleType, resourceType, multimap, headers);
|
||||
}
|
||||
|
||||
// Paging starts here
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,7 +38,6 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import com.google.common.collect.Lists;
|
||||
|
@ -61,6 +60,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
|
@ -70,6 +70,7 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -77,16 +78,28 @@ import java.util.stream.Collectors;
|
|||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.substring;
|
||||
|
||||
public class FhirTerser {
|
||||
|
||||
private static final Pattern COMPARTMENT_MATCHER_PATH =
|
||||
Pattern.compile("([a-zA-Z.]+)\\.where\\(resolve\\(\\) is ([a-zA-Z]+)\\)");
|
||||
|
||||
private static final String USER_DATA_KEY_CONTAIN_RESOURCES_COMPLETED =
|
||||
FhirTerser.class.getName() + "_CONTAIN_RESOURCES_COMPLETED";
|
||||
|
||||
private final FhirContext myContext;
|
||||
|
||||
/**
|
||||
* This comparator sorts IBaseReferences, and places any that are missing an ID at the end. Those with an ID go to the front.
|
||||
*/
|
||||
private static final Comparator<IBaseReference> REFERENCES_WITH_IDS_FIRST =
|
||||
Comparator.nullsLast(Comparator.comparing(ref -> {
|
||||
if (ref.getResource() == null) return true;
|
||||
if (ref.getResource().getIdElement() == null) return true;
|
||||
if (ref.getResource().getIdElement().getValue() == null) return true;
|
||||
return false;
|
||||
}));
|
||||
|
||||
public FhirTerser(FhirContext theContext) {
|
||||
super();
|
||||
myContext = theContext;
|
||||
|
@ -1418,6 +1431,13 @@ public class FhirTerser {
|
|||
private void containResourcesForEncoding(
|
||||
ContainedResources theContained, IBaseResource theResource, boolean theModifyResource) {
|
||||
List<IBaseReference> allReferences = getAllPopulatedChildElementsOfType(theResource, IBaseReference.class);
|
||||
|
||||
// Note that we process all contained resources that have arrived here with an ID contained resources first, so
|
||||
// that we don't accidentally auto-assign an ID
|
||||
// which may collide with a resource we have yet to process.
|
||||
// See: https://github.com/hapifhir/hapi-fhir/issues/6403
|
||||
allReferences.sort(REFERENCES_WITH_IDS_FIRST);
|
||||
|
||||
for (IBaseReference next : allReferences) {
|
||||
IBaseResource resource = next.getResource();
|
||||
if (resource == null && next.getReferenceElement().isLocal()) {
|
||||
|
@ -1437,11 +1457,11 @@ public class FhirTerser {
|
|||
IBaseResource resource = next.getResource();
|
||||
if (resource != null) {
|
||||
if (resource.getIdElement().isEmpty() || resource.getIdElement().isLocal()) {
|
||||
if (theContained.getResourceId(resource) != null) {
|
||||
// Prevent infinite recursion if there are circular loops in the contained resources
|
||||
|
||||
IIdType id = theContained.addContained(resource);
|
||||
if (id == null) {
|
||||
continue;
|
||||
}
|
||||
IIdType id = theContained.addContained(resource);
|
||||
if (theModifyResource) {
|
||||
getContainedResourceList(theResource).add(resource);
|
||||
next.setReference(id.getValue());
|
||||
|
@ -1768,8 +1788,6 @@ public class FhirTerser {
|
|||
}
|
||||
|
||||
public static class ContainedResources {
|
||||
private long myNextContainedId = 1;
|
||||
|
||||
private List<IBaseResource> myResourceList;
|
||||
private IdentityHashMap<IBaseResource, IIdType> myResourceToIdMap;
|
||||
private Map<String, IBaseResource> myExistingIdToContainedResourceMap;
|
||||
|
@ -1782,6 +1800,11 @@ public class FhirTerser {
|
|||
}
|
||||
|
||||
public IIdType addContained(IBaseResource theResource) {
|
||||
if (this.getResourceId(theResource) != null) {
|
||||
// Prevent infinite recursion if there are circular loops in the contained resources
|
||||
return null;
|
||||
}
|
||||
|
||||
IIdType existing = getResourceToIdMap().get(theResource);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
|
@ -1789,16 +1812,7 @@ public class FhirTerser {
|
|||
|
||||
IIdType newId = theResource.getIdElement();
|
||||
if (isBlank(newId.getValue())) {
|
||||
newId.setValue("#" + myNextContainedId++);
|
||||
} else {
|
||||
// Avoid auto-assigned contained IDs colliding with pre-existing ones
|
||||
String idPart = newId.getValue();
|
||||
if (substring(idPart, 0, 1).equals("#")) {
|
||||
idPart = idPart.substring(1);
|
||||
if (StringUtils.isNumeric(idPart)) {
|
||||
myNextContainedId = Long.parseLong(idPart) + 1;
|
||||
}
|
||||
}
|
||||
newId.setValue("#" + UUID.randomUUID());
|
||||
}
|
||||
|
||||
getResourceToIdMap().put(theResource, newId);
|
||||
|
@ -1862,45 +1876,5 @@ public class FhirTerser {
|
|||
public boolean hasExistingIdToContainedResource() {
|
||||
return myExistingIdToContainedResourceMap != null;
|
||||
}
|
||||
|
||||
public void assignIdsToContainedResources() {
|
||||
|
||||
if (!getContainedResources().isEmpty()) {
|
||||
|
||||
/*
|
||||
* The idea with the code block below:
|
||||
*
|
||||
* We want to preserve any IDs that were user-assigned, so that if it's really
|
||||
* important to someone that their contained resource have the ID of #FOO
|
||||
* or #1 we will keep that.
|
||||
*
|
||||
* For any contained resources where no ID was assigned by the user, we
|
||||
* want to manually create an ID but make sure we don't reuse an existing ID.
|
||||
*/
|
||||
|
||||
Set<String> ids = new HashSet<>();
|
||||
|
||||
// Gather any user assigned IDs
|
||||
for (IBaseResource nextResource : getContainedResources()) {
|
||||
if (getResourceToIdMap().get(nextResource) != null) {
|
||||
ids.add(getResourceToIdMap().get(nextResource).getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically assign IDs to the rest
|
||||
for (IBaseResource nextResource : getContainedResources()) {
|
||||
|
||||
while (getResourceToIdMap().get(nextResource) == null) {
|
||||
String nextCandidate = "#" + myNextContainedId;
|
||||
myNextContainedId++;
|
||||
if (!ids.add(nextCandidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
getResourceToIdMap().put(nextResource, new IdDt(nextCandidate));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,6 +170,7 @@ public enum VersionEnum {
|
|||
|
||||
V7_5_0,
|
||||
V7_6_0,
|
||||
V7_6_1,
|
||||
V7_7_0,
|
||||
V7_8_0;
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.util.adapters;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class AdapterManager implements IAdapterManager {
|
||||
public static final AdapterManager INSTANCE = new AdapterManager();
|
||||
|
||||
Set<IAdapterFactory> myAdapterFactories = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Hidden to force shared use of the public INSTANCE.
|
||||
*/
|
||||
AdapterManager() {}
|
||||
|
||||
public <T> @Nonnull Optional<T> getAdapter(Object theObject, Class<T> theTargetType) {
|
||||
// todo this can be sped up with a cache of type->Factory.
|
||||
return myAdapterFactories.stream()
|
||||
.filter(nextFactory -> nextFactory.getAdapters().stream().anyMatch(theTargetType::isAssignableFrom))
|
||||
.flatMap(nextFactory -> {
|
||||
var adapter = nextFactory.getAdapter(theObject, theTargetType);
|
||||
// can't use Optional.stream() because of our Android target is API level 26/JDK 8.
|
||||
if (adapter.isPresent()) {
|
||||
return Stream.of(adapter.get());
|
||||
} else {
|
||||
return Stream.empty();
|
||||
}
|
||||
})
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public void registerFactory(@Nonnull IAdapterFactory theFactory) {
|
||||
myAdapterFactories.add(theFactory);
|
||||
}
|
||||
|
||||
public void unregisterFactory(@Nonnull IAdapterFactory theFactory) {
|
||||
myAdapterFactories.remove(theFactory);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.util.adapters;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class AdapterUtils {
|
||||
|
||||
/**
|
||||
* Main entry point for adapter calls.
|
||||
* Implements three conversions: cast to the target type, use IAdaptable if present, or lastly try the AdapterManager.INSTANCE.
|
||||
* @param theObject the object to be adapted
|
||||
* @param theTargetType the type of the adapter requested
|
||||
*/
|
||||
static <T> Optional<T> adapt(Object theObject, Class<T> theTargetType) {
|
||||
if (theTargetType.isInstance(theObject)) {
|
||||
//noinspection unchecked
|
||||
return Optional.of((T) theObject);
|
||||
}
|
||||
|
||||
if (theObject instanceof IAdaptable) {
|
||||
IAdaptable adaptable = (IAdaptable) theObject;
|
||||
var adapted = adaptable.getAdapter(theTargetType);
|
||||
if (adapted.isPresent()) {
|
||||
return adapted;
|
||||
}
|
||||
}
|
||||
|
||||
return AdapterManager.INSTANCE.getAdapter(theObject, theTargetType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.util.adapters;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Generic version of Eclipse IAdaptable interface.
|
||||
*/
|
||||
public interface IAdaptable {
|
||||
/**
|
||||
* Get an adapter of requested type.
|
||||
* @param theTargetType the desired type of the adapter
|
||||
* @return an adapter of theTargetType if possible, or empty.
|
||||
*/
|
||||
default <T> @Nonnull Optional<T> getAdapter(@Nonnull Class<T> theTargetType) {
|
||||
return AdapterUtils.adapt(this, theTargetType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.util.adapters;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Interface for external service that builds adaptors for targets.
|
||||
*/
|
||||
public interface IAdapterFactory {
|
||||
/**
|
||||
* Build an adaptor for the target.
|
||||
* May return empty() even if the target type is listed in getAdapters() when
|
||||
* the factory fails to convert a particular instance.
|
||||
*
|
||||
* @param theObject the object to be adapted.
|
||||
* @param theAdapterType the target type
|
||||
* @return the adapter, if possible.
|
||||
*/
|
||||
<T> Optional<T> getAdapter(Object theObject, Class<T> theAdapterType);
|
||||
|
||||
/**
|
||||
* @return the collection of adapter target types handled by this factory.
|
||||
*/
|
||||
Collection<Class<?>> getAdapters();
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.util.adapters;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Get an adaptor
|
||||
*/
|
||||
public interface IAdapterManager {
|
||||
<T> Optional<T> getAdapter(Object theTarget, Class<T> theAdapter);
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 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%
|
||||
*/
|
||||
/**
|
||||
* Implements the Adapter pattern to allow external classes to extend/adapt existing classes.
|
||||
* Useful for extending interfaces that are closed to modification, or restricted for classpath reasons.
|
||||
* <p>
|
||||
* For clients, the main entry point is {@link ca.uhn.fhir.util.adapters.AdapterUtils#adapt(java.lang.Object, java.lang.Class)}
|
||||
* which will attempt to cast to the target type, or build an adapter of the target type.
|
||||
* </p>
|
||||
* <p>
|
||||
* For implementors, you can support adaptation via two mechanisms:
|
||||
* <ul>
|
||||
* <li>by implementing {@link ca.uhn.fhir.util.adapters.IAdaptable} directly on a class to provide supported adapters,
|
||||
* <li>or when the class is closed to direct modification, you can implement
|
||||
* an instance of {@link ca.uhn.fhir.util.adapters.IAdapterFactory} and register
|
||||
* it with the public {@link ca.uhn.fhir.util.adapters.AdapterManager#INSTANCE}.</li>
|
||||
* </ul>
|
||||
* The AdapterUtils.adapt() supports both of these.
|
||||
* </p>
|
||||
* Inspired by the Eclipse runtime.
|
||||
*/
|
||||
package ca.uhn.fhir.util.adapters;
|
|
@ -0,0 +1,78 @@
|
|||
package ca.uhn.fhir.util.adapters;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class AdapterManagerTest {
|
||||
AdapterManager myAdapterManager = new AdapterManager();
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
assertThat(AdapterManager.INSTANCE.myAdapterFactories)
|
||||
.withFailMessage("Don't dirty the public instance").isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterFactory_providesAdapter() {
|
||||
// given
|
||||
myAdapterManager.registerFactory(new StringToIntFactory());
|
||||
|
||||
// when
|
||||
var result = myAdapterManager.getAdapter("22", Integer.class);
|
||||
|
||||
// then
|
||||
assertThat(result).contains(22);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegisterFactory_wrongTypeStillEmpty() {
|
||||
// given
|
||||
myAdapterManager.registerFactory(new StringToIntFactory());
|
||||
|
||||
// when
|
||||
var result = myAdapterManager.getAdapter("22", Float.class);
|
||||
|
||||
// then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnregisterFactory_providesEmpty() {
|
||||
// given active factory, now gone.
|
||||
StringToIntFactory factory = new StringToIntFactory();
|
||||
myAdapterManager.registerFactory(factory);
|
||||
myAdapterManager.getAdapter("22", Integer.class);
|
||||
myAdapterManager.unregisterFactory(factory);
|
||||
|
||||
// when
|
||||
var result = myAdapterManager.getAdapter("22", Integer.class);
|
||||
|
||||
// then
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
static class StringToIntFactory implements IAdapterFactory {
|
||||
@Override
|
||||
public <T> Optional<T> getAdapter(Object theObject, Class<T> theAdapterType) {
|
||||
if (theObject instanceof String s) {
|
||||
if (theAdapterType.isAssignableFrom(Integer.class)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T i = (T) Integer.valueOf(s);
|
||||
return Optional.of(i);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public Collection<Class<?>> getAdapters() {
|
||||
return List.of(Integer.class);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package ca.uhn.fhir.util.adapters;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AdapterUtilsTest {
|
||||
|
||||
final private IAdapterFactory myTestFactory = new TestAdaptorFactory();
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
AdapterManager.INSTANCE.unregisterFactory(myTestFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullDoesNotAdapt() {
|
||||
|
||||
// when
|
||||
var adapted = AdapterUtils.adapt(null, InterfaceA.class);
|
||||
|
||||
// then
|
||||
assertThat(adapted).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdaptObjectImplementingInterface() {
|
||||
// given
|
||||
var object = new ClassB();
|
||||
|
||||
// when
|
||||
var adapted = AdapterUtils.adapt(object, InterfaceA.class);
|
||||
|
||||
// then
|
||||
assertThat(adapted)
|
||||
.isPresent()
|
||||
.get().isInstanceOf(InterfaceA.class);
|
||||
assertThat(adapted.get()).withFailMessage("Use object since it implements interface").isSameAs(object);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdaptObjectImplementingAdaptorSupportingInterface() {
|
||||
// given
|
||||
var object = new SelfAdaptableClass();
|
||||
|
||||
// when
|
||||
var adapted = AdapterUtils.adapt(object, InterfaceA.class);
|
||||
|
||||
// then
|
||||
assertThat(adapted)
|
||||
.isPresent()
|
||||
.get().isInstanceOf(InterfaceA.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdaptObjectViaAdapterManager() {
|
||||
// given
|
||||
var object = new ManagerAdaptableClass();
|
||||
AdapterManager.INSTANCE.registerFactory(myTestFactory);
|
||||
|
||||
// when
|
||||
var adapted = AdapterUtils.adapt(object, InterfaceA.class);
|
||||
|
||||
// then
|
||||
assertThat(adapted)
|
||||
.isPresent()
|
||||
.get().isInstanceOf(InterfaceA.class);
|
||||
}
|
||||
|
||||
interface InterfaceA {
|
||||
|
||||
}
|
||||
|
||||
static class ClassB implements InterfaceA {
|
||||
|
||||
}
|
||||
|
||||
/** class that can adapt itself to IAdaptable */
|
||||
static class SelfAdaptableClass implements IAdaptable {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public <T> Optional<T> getAdapter(@Nonnull Class<T> theTargetType) {
|
||||
if (theTargetType.isAssignableFrom(InterfaceA.class)) {
|
||||
T value = theTargetType.cast(buildInterfaceAWrapper(this));
|
||||
return Optional.of(value);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nonnull InterfaceA buildInterfaceAWrapper(Object theObject) {
|
||||
return new InterfaceA() {};
|
||||
}
|
||||
|
||||
/** Class that relies on an external IAdapterFactory */
|
||||
static class ManagerAdaptableClass {
|
||||
}
|
||||
|
||||
|
||||
static class TestAdaptorFactory implements IAdapterFactory {
|
||||
|
||||
@Override
|
||||
public <T> Optional<T> getAdapter(Object theObject, Class<T> theAdapterType) {
|
||||
if (theObject instanceof ManagerAdaptableClass && theAdapterType == InterfaceA.class) {
|
||||
T adapter = theAdapterType.cast(buildInterfaceAWrapper(theObject));
|
||||
return Optional.of(adapter);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Class<?>> getAdapters() {
|
||||
return Set.of(InterfaceA.class);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
type: change
|
||||
jira: SMILE-9161
|
||||
title: "Contained resources which arrive without assigned IDs are now assigned GUIDs, as opposed to monotonically increasing numeric IDs. This avoids a whole class of issues related to processing order and collisions."
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
jira: SMILE-9161
|
||||
title: "Fixed a rare bug in the JSON Parser, wherein client-assigned contained resource IDs could collide with server-assigned contained IDs. For example if a
|
||||
resource had a client-assigned contained ID of `#2`, and a contained resource with no ID, then depending on the processing order, the parser could occasionally
|
||||
provide duplicate contained resource IDs, leading to non-deterministic behaviour."
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 6419
|
||||
title: "Previously, on Postgres, the `$reindex` operation with `optimizeStorage` set to `ALL_VERSIONS` would process
|
||||
only a subset of versions if there were more than 100 versions to be processed for a resource. This has been fixed
|
||||
so that all versions of the resource are now processed."
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 6420
|
||||
title: "Previously, when the `$reindex` operation is run for a single FHIR resource with `optimizeStorage` set to
|
||||
`ALL_VERSIONS`, none of the versions of the resource were processed in `hfj_res_ver` table. This has been fixed."
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 6422
|
||||
title: "Previously, since 7.4.4 the validation issue detail codes were not translated correctly for Remote Terminology
|
||||
validateCode calls. The detail code used was `invalid-code` for all use-cases which resulted in profile binding strength
|
||||
not being applied to the issue severity as expected when validating resources against a profile.
|
||||
This has been fixed and issue detail codes are translated correctly."
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 6440
|
||||
title: "Previously, if an `IInterceptorBroadcaster` was set in a `RequestDetails` object,
|
||||
`STORAGE_PRECHECK_FOR_CACHED_SEARCH` hooks that were registered to that `IInterceptorBroadcaster` were not
|
||||
called. Also, if an `IInterceptorBroadcaster` was set in the `RequestDetails` object, the boolean return value of the hooks
|
||||
registered to that `IInterceptorBroadcaster` were not taken into account. This second issue existed for all pointcuts
|
||||
that returned a boolean type, not just for `STORAGE_PRECHECK_FOR_CACHED_SEARCH`. These issues have now been fixed."
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
type: add
|
||||
issue: 6445
|
||||
title: "Add Multimap versions of the search() methods to Repository to support queries like `Patient?_tag=a&_tag=b`"
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 6451
|
||||
jira: SMILE-9089
|
||||
title: "Previously, activating `BulkDataImport` job would not change jobs status to `RUNNING`,
|
||||
causing it to be processed multiple times instead of single time. This has been fixed."
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 6467
|
||||
title: "Fixed an incompatibility between Hibernate Search and Lucene versions that caused ValueSet expansion to fail
|
||||
when Hibernate Search was configured to use Lucene."
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
type: perf
|
||||
issue: 6489
|
||||
title: "Change the migrator to avoid table locks when adding an index. This allows systems to continue running during upgrade."
|
|
@ -4,7 +4,7 @@
|
|||
title: "The version of a few dependencies have been bumped to more recent versions
|
||||
(dependent HAPI modules listed in brackets):
|
||||
<ul>
|
||||
<li>org.hl7.fhir.core (Base): 6.3.18 -> 6.3.25</li>
|
||||
<li>org.hl7.fhir.core (Base): 6.3.18 -> 6.4.0</li>
|
||||
<li>Bower/Moment.js (hapi-fhir-testpage-overlay): 2.27.0 -> 2.29.4</li>
|
||||
<li>htmlunit (Base): 3.9.0 -> 3.11.0</li>
|
||||
<li>Elasticsearch (Base): 8.11.1 -> 8.14.3</li>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: fix
|
||||
issue: 6502
|
||||
backport: 7.6.1
|
||||
title: "Support ReferenceParam in addition to UriParam for `_profile` in queries using the SearchParameterMap to match the change in the specification from DSTU3 to R4."
|
|
@ -49,15 +49,15 @@ Additional parameters have been added to support CQL evaluation.
|
|||
The following parameters are supported for the `Questionnaire/$populate` operation:
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|---------------------|---------------|-------------|
|
||||
|---------------------|--------------------|-------------|
|
||||
| questionnaire | Questionnaire | The Questionnaire to populate. Used when the operation is invoked at the 'type' level. |
|
||||
| canonical | canonical | The canonical identifier for the Questionnaire (optionally version-specific). |
|
||||
| url | uri | Canonical URL of the Questionnaire when invoked at the resource type level. This is exclusive with the questionnaire and canonical parameters. |
|
||||
| version | string | Version of the Questionnaire when invoked at the resource type level. This is exclusive with the questionnaire and canonical parameters. |
|
||||
| subject | Reference | The resource that is to be the QuestionnaireResponse.subject. The QuestionnaireResponse instance will reference the provided subject. |
|
||||
| context | | Resources containing information to be used to help populate the QuestionnaireResponse. |
|
||||
| context.name | string | The name of the launchContext or root Questionnaire variable the passed content should be used as for population purposes. The name SHALL correspond to a launchContext or variable delared at the root of the Questionnaire. |
|
||||
| context.reference | Reference | The actual resource (or resources) to use as the value of the launchContext or variable. |
|
||||
| context.name | string | The name of the launchContext or root Questionnaire variable the passed content should be used as for population purposes. The name SHALL correspond to a launchContext or variable declared at the root of the Questionnaire. |
|
||||
| context.content | Reference/Resource | The actual resource (or reference) to use as the value of the launchContext or variable. |
|
||||
| local | boolean | Whether the server should use what resources and other knowledge it has about the referenced subject when pre-populating answers to questions. |
|
||||
| launchContext | Extension | The [Questionnaire Launch Context](https://hl7.org/fhir/uv/sdc/StructureDefinition-sdc-questionnaire-launchContext.html) extension containing Resources that provide context for form processing logic (pre-population) when creating/displaying/editing a QuestionnaireResponse. |
|
||||
| parameters | Parameters | Any input parameters defined in libraries referenced by the Questionnaire. |
|
||||
|
|
|
@ -217,6 +217,12 @@ public class BulkDataImportSvcImpl implements IBulkDataImportSvc, IHasScheduledJ
|
|||
String biJobId = null;
|
||||
try {
|
||||
biJobId = processJob(bulkImportJobEntity);
|
||||
// set job status to RUNNING so it would not be processed again
|
||||
myTxTemplate.execute(t -> {
|
||||
bulkImportJobEntity.setStatus(BulkImportJobStatusEnum.RUNNING);
|
||||
myJobDao.save(bulkImportJobEntity);
|
||||
return null;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failure while preparing bulk export extract", e);
|
||||
myTxTemplate.execute(t -> {
|
||||
|
@ -256,6 +262,7 @@ public class BulkDataImportSvcImpl implements IBulkDataImportSvc, IHasScheduledJ
|
|||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public JobInfo getJobStatus(String theBiJobId) {
|
||||
BulkImportJobEntity theJob = findJobByBiJobId(theBiJobId);
|
||||
return new JobInfo()
|
||||
|
|
|
@ -134,6 +134,7 @@ import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -1697,9 +1698,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
if (theOptimizeStorageMode == ReindexParameters.OptimizeStorageModeEnum.ALL_VERSIONS) {
|
||||
int pageSize = 100;
|
||||
for (int page = 0; ((long) page * pageSize) < entity.getVersion(); page++) {
|
||||
|
||||
// We need to sort the pages, because we are updating the same data we are paging through.
|
||||
// If not sorted explicitly, a database like Postgres returns the same data multiple times on
|
||||
// different pages as the underlying data gets updated.
|
||||
PageRequest pageRequest = PageRequest.of(page, pageSize, Sort.by("myId"));
|
||||
Slice<ResourceHistoryTable> historyEntities =
|
||||
myResourceHistoryTableDao.findForResourceIdAndReturnEntitiesAndFetchProvenance(
|
||||
PageRequest.of(page, pageSize), entity.getId(), historyEntity.getVersion());
|
||||
pageRequest, entity.getId(), historyEntity.getVersion());
|
||||
|
||||
for (ResourceHistoryTable next : historyEntities) {
|
||||
reindexOptimizeStorageHistoryEntity(entity, next);
|
||||
}
|
||||
|
|
|
@ -205,7 +205,7 @@ public abstract class BaseHapiFhirSystemDao<T extends IBaseBundle, MT> extends B
|
|||
*
|
||||
* However, for realistic average workloads, this should reduce the number of round trips.
|
||||
*/
|
||||
if (idChunk.size() >= 2) {
|
||||
if (!idChunk.isEmpty()) {
|
||||
List<ResourceTable> entityChunk = prefetchResourceTableHistoryAndProvenance(idChunk);
|
||||
|
||||
if (thePreFetchIndexes) {
|
||||
|
|
|
@ -150,6 +150,6 @@ public interface IBatch2WorkChunkRepository
|
|||
@Param("status") WorkChunkStatusEnum theStatus);
|
||||
|
||||
@Query(
|
||||
"SELECT new ca.uhn.fhir.batch2.model.BatchWorkChunkStatusDTO(e.myTargetStepId, e.myStatus, min(e.myStartTime), max(e.myEndTime), avg(e.myEndTime - e.myStartTime), count(*)) FROM Batch2WorkChunkEntity e WHERE e.myInstanceId=:instanceId GROUP BY e.myTargetStepId, e.myStatus")
|
||||
"SELECT new ca.uhn.fhir.batch2.model.BatchWorkChunkStatusDTO(e.myTargetStepId, e.myStatus, min(e.myStartTime), max(e.myEndTime), avg(cast((e.myEndTime - e.myStartTime) as long)), count(*)) FROM Batch2WorkChunkEntity e WHERE e.myInstanceId=:instanceId GROUP BY e.myTargetStepId, e.myStatus")
|
||||
List<BatchWorkChunkStatusDTO> fetchWorkChunkStatusForInstance(@Param("instanceId") String theInstanceId);
|
||||
}
|
||||
|
|
|
@ -605,12 +605,12 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc<JpaPid> {
|
|||
.add(SearchParameterMap.class, theParams)
|
||||
.add(RequestDetails.class, theRequestDetails)
|
||||
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails);
|
||||
Object outcome = CompositeInterceptorBroadcaster.doCallHooksAndReturnObject(
|
||||
boolean canUseCache = CompositeInterceptorBroadcaster.doCallHooks(
|
||||
myInterceptorBroadcaster,
|
||||
theRequestDetails,
|
||||
Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH,
|
||||
params);
|
||||
if (Boolean.FALSE.equals(outcome)) {
|
||||
if (!canUseCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -210,7 +210,7 @@ public class QueryStack {
|
|||
CoordsPredicateBuilder coordsBuilder = (CoordsPredicateBuilder) builder;
|
||||
|
||||
List<List<IQueryParameterType>> params = theParams.get(theParamName);
|
||||
if (params.size() > 0 && params.get(0).size() > 0) {
|
||||
if (!params.isEmpty() && !params.get(0).isEmpty()) {
|
||||
IQueryParameterType param = params.get(0).get(0);
|
||||
ParsedLocationParam location = ParsedLocationParam.from(theParams, param);
|
||||
double latitudeValue = location.getLatitudeValue();
|
||||
|
@ -2134,6 +2134,10 @@ public class QueryStack {
|
|||
if (nextParam.getModifier() == TokenParamModifier.NOT) {
|
||||
paramInverted = true;
|
||||
}
|
||||
} else if (nextOrParam instanceof ReferenceParam) {
|
||||
ReferenceParam nextParam = (ReferenceParam) nextOrParam;
|
||||
code = nextParam.getValue();
|
||||
system = null;
|
||||
} else {
|
||||
UriParam nextParam = (UriParam) nextOrParam;
|
||||
code = nextParam.getValue();
|
||||
|
@ -2160,8 +2164,10 @@ public class QueryStack {
|
|||
}
|
||||
}
|
||||
|
||||
UriParam nextParam = (UriParam) nextParamUncasted;
|
||||
if (isNotBlank(nextParam.getValue())) {
|
||||
if (nextParamUncasted instanceof ReferenceParam
|
||||
&& isNotBlank(((ReferenceParam) nextParamUncasted).getValue())) {
|
||||
return true;
|
||||
} else if (nextParamUncasted instanceof UriParam && isNotBlank(((UriParam) nextParamUncasted).getValue())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1056,7 +1056,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
if (theExpansionOptions != null
|
||||
&& !theExpansionOptions.isFailOnMissingCodeSystem()
|
||||
// Code system is unknown, therefore NOT_FOUND
|
||||
&& e.getCodeValidationIssue().getCoding() == CodeValidationIssueCoding.NOT_FOUND) {
|
||||
&& e.getCodeValidationIssue()
|
||||
.hasIssueDetailCode(CodeValidationIssueCoding.NOT_FOUND.getCode())) {
|
||||
return;
|
||||
}
|
||||
throw new InternalErrorException(Msg.code(888) + e);
|
||||
|
@ -2203,7 +2204,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs {
|
|||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setCodeSystemVersion(theCodeSystemVersion)
|
||||
.setMessage(theMessage)
|
||||
.addCodeValidationIssue(new CodeValidationIssue(
|
||||
.addIssue(new CodeValidationIssue(
|
||||
theMessage,
|
||||
IssueSeverity.ERROR,
|
||||
CodeValidationIssueCode.CODE_INVALID,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.mdm.helper;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
|
@ -23,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import java.util.function.Supplier;
|
||||
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
|
@ -78,6 +80,7 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
|
|||
//they are coming from an external HTTP Request.
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(myMockSrd.getInterceptorBroadcaster()).thenReturn(myMockInterceptorBroadcaster);
|
||||
when(myMockInterceptorBroadcaster.callHooks(any(Pointcut.class), any(HookParams.class))).thenReturn(true);
|
||||
when(myMockSrd.getServletRequest()).thenReturn(myMockServletRequest);
|
||||
when(myMockSrd.getServer()).thenReturn(myMockRestfulServer);
|
||||
when(myMockSrd.getRequestId()).thenReturn("MOCK_REQUEST");
|
||||
|
|
|
@ -881,6 +881,11 @@ public class JpaJobPersistenceImplTest extends BaseJpaR4Test {
|
|||
public void testFetchInstanceAndWorkChunkStatus() {
|
||||
// Setup
|
||||
|
||||
Date date1 = new Date();
|
||||
Date date2 = new Date();
|
||||
|
||||
|
||||
|
||||
List<String> chunkIds = new ArrayList<>();
|
||||
JobInstance instance = createInstance();
|
||||
String instanceId = mySvc.storeNewInstance(instance);
|
||||
|
|
|
@ -17,6 +17,7 @@ import ca.uhn.fhir.jpa.bulk.imprt.api.IBulkDataImportSvc;
|
|||
import ca.uhn.fhir.jpa.bulk.imprt.model.ActivateJobResult;
|
||||
import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobFileJson;
|
||||
import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobJson;
|
||||
import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobStatusEnum;
|
||||
import ca.uhn.fhir.jpa.bulk.imprt.model.JobFileRowProcessingModeEnum;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelConsumerSettings;
|
||||
|
@ -166,6 +167,8 @@ public class BulkDataImportR4Test extends BaseJpaR4Test implements ITestDataBuil
|
|||
|
||||
ActivateJobResult activateJobOutcome = mySvc.activateNextReadyJob();
|
||||
assertTrue(activateJobOutcome.isActivated);
|
||||
// validate that job changed status from READY to RUNNING
|
||||
assertEquals(BulkImportJobStatusEnum.RUNNING, mySvc.getJobStatus(jobId).getStatus());
|
||||
|
||||
JobInstance instance = myBatch2JobHelper.awaitJobCompletion(activateJobOutcome.jobId, 60);
|
||||
assertNotNull(instance);
|
||||
|
@ -196,6 +199,8 @@ public class BulkDataImportR4Test extends BaseJpaR4Test implements ITestDataBuil
|
|||
|
||||
ActivateJobResult activateJobOutcome = mySvc.activateNextReadyJob();
|
||||
assertTrue(activateJobOutcome.isActivated);
|
||||
// validate that job changed status from READY to RUNNING
|
||||
assertEquals(BulkImportJobStatusEnum.RUNNING, mySvc.getJobStatus(jobId).getStatus());
|
||||
|
||||
JobInstance instance = myBatch2JobHelper.awaitJobCompletion(activateJobOutcome.jobId);
|
||||
assertNotNull(instance);
|
||||
|
|
|
@ -62,6 +62,9 @@ public abstract class BaseComboParamsR4Test extends BaseJpaR4Test {
|
|||
myMessages.add("REUSING CACHED SEARCH");
|
||||
return null;
|
||||
});
|
||||
|
||||
// allow searches to use cached results
|
||||
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH), ArgumentMatchers.any(HookParams.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
|
@ -23,6 +24,8 @@ import org.hl7.fhir.r4.model.Reference;
|
|||
import org.hl7.fhir.r4.model.ServiceRequest;
|
||||
import org.hl7.fhir.r4.model.ServiceRequest.ServiceRequestIntent;
|
||||
import org.hl7.fhir.r4.model.ServiceRequest.ServiceRequestStatus;
|
||||
import org.hl7.fhir.r4.model.Specimen;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
|
@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.BundleBuilder;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -81,6 +82,7 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.test.utilities.UuidUtils.HASH_UUID_PATTERN;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -738,7 +740,8 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
|
|||
|
||||
String encoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
ourLog.info("Input: {}", encoded);
|
||||
assertThat(encoded).contains("#1");
|
||||
String organizationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuid);
|
||||
|
||||
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
||||
|
||||
|
@ -746,10 +749,12 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test {
|
|||
|
||||
encoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
ourLog.info("Output: {}", encoded);
|
||||
assertThat(encoded).contains("#1");
|
||||
String organizationUuidParsed = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuidParsed);
|
||||
assertEquals(organizationUuid, organizationUuidParsed);
|
||||
|
||||
Organization org = (Organization) p.getManagingOrganization().getResource();
|
||||
assertEquals("#1", org.getId());
|
||||
assertEquals("#" + organizationUuid, org.getId());
|
||||
assertThat(org.getMeta().getTag()).hasSize(1);
|
||||
|
||||
}
|
||||
|
|
|
@ -2722,7 +2722,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
|
|||
ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(output));
|
||||
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals(3, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||
myCaptureQueriesListener.logInsertQueriesForCurrentThread();
|
||||
assertEquals(2, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
||||
myCaptureQueriesListener.logUpdateQueriesForCurrentThread();
|
||||
|
|
|
@ -428,7 +428,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
|||
IdType observationId = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
|
||||
|
||||
// Make sure we're not introducing any extra DB operations
|
||||
assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(3);
|
||||
assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(2);
|
||||
|
||||
// Read back and verify that reference is now versioned
|
||||
observation = myObservationDao.read(observationId);
|
||||
|
@ -463,7 +463,7 @@ public class FhirResourceDaoR4VersionedReferenceTest extends BaseJpaR4Test {
|
|||
IdType observationId = new IdType(outcome.getEntry().get(1).getResponse().getLocation());
|
||||
|
||||
// Make sure we're not introducing any extra DB operations
|
||||
assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(4);
|
||||
assertThat(myCaptureQueriesListener.logSelectQueries()).hasSize(3);
|
||||
|
||||
// Read back and verify that reference is now versioned
|
||||
observation = myObservationDao.read(observationId);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.dao.r4;
|
||||
|
||||
import static ca.uhn.fhir.test.utilities.UuidUtils.HASH_UUID_PATTERN;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
@ -3219,8 +3220,8 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
|
|||
String id = outcome.getEntry().get(0).getResponse().getLocation();
|
||||
patient = myPatientDao.read(new IdType(id));
|
||||
|
||||
assertEquals("#1", patient.getManagingOrganization().getReference());
|
||||
assertEquals("#1", patient.getContained().get(0).getId());
|
||||
assertThat(patient.getManagingOrganization().getReference()).containsPattern(HASH_UUID_PATTERN);
|
||||
assertEquals(patient.getManagingOrganization().getReference(), patient.getContained().get(0).getId());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
|
|
@ -263,6 +263,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testParameterWithNoValueThrowsError_InvalidChainOnCustomSearch() throws IOException {
|
||||
SearchParameter searchParameter = new SearchParameter();
|
||||
|
|
|
@ -180,6 +180,59 @@ public class ReindexTaskTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptimizeStorage_AllVersions_SingleResourceWithMultipleVersion() {
|
||||
|
||||
// this difference of this test from testOptimizeStorage_AllVersions is that this one has only 1 resource
|
||||
// (with multiple versions) in the db. There was a bug where if only one resource were being re-indexed, the
|
||||
// resource wasn't processed for optimize storage.
|
||||
|
||||
// Setup
|
||||
IIdType patientId = createPatient(withActiveTrue());
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Patient p = new Patient();
|
||||
p.setId(patientId.toUnqualifiedVersionless());
|
||||
p.setActive(true);
|
||||
p.addIdentifier().setValue(String.valueOf(i));
|
||||
myPatientDao.update(p, mySrd);
|
||||
}
|
||||
|
||||
// Move resource text to compressed storage, which we don't write to anymore but legacy
|
||||
// data may exist that was previously stored there, so we're simulating that.
|
||||
List<ResourceHistoryTable> allHistoryEntities = runInTransaction(() -> myResourceHistoryTableDao.findAll());
|
||||
allHistoryEntities.forEach(t->relocateResourceTextToCompressedColumn(t.getResourceId(), t.getVersion()));
|
||||
|
||||
runInTransaction(()->{
|
||||
assertEquals(11, myResourceHistoryTableDao.count());
|
||||
for (ResourceHistoryTable history : myResourceHistoryTableDao.findAll()) {
|
||||
assertNull(history.getResourceTextVc());
|
||||
assertNotNull(history.getResource());
|
||||
}
|
||||
});
|
||||
|
||||
// execute
|
||||
JobInstanceStartRequest startRequest = new JobInstanceStartRequest();
|
||||
startRequest.setJobDefinitionId(JOB_REINDEX);
|
||||
startRequest.setParameters(
|
||||
new ReindexJobParameters()
|
||||
.setOptimizeStorage(ReindexParameters.OptimizeStorageModeEnum.ALL_VERSIONS)
|
||||
.setReindexSearchParameters(ReindexParameters.ReindexSearchParametersEnum.NONE)
|
||||
);
|
||||
Batch2JobStartResponse startResponse = myJobCoordinator.startInstance(mySrd, startRequest);
|
||||
myBatch2JobHelper.awaitJobCompletion(startResponse);
|
||||
|
||||
// validate
|
||||
runInTransaction(()->{
|
||||
assertEquals(11, myResourceHistoryTableDao.count());
|
||||
for (ResourceHistoryTable history : myResourceHistoryTableDao.findAll()) {
|
||||
assertNotNull(history.getResourceTextVc());
|
||||
assertNull(history.getResource());
|
||||
}
|
||||
});
|
||||
Patient patient = myPatientDao.read(patientId, mySrd);
|
||||
assertTrue(patient.getActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptimizeStorage_AllVersions_CopyProvenanceEntityData() {
|
||||
// Setup
|
||||
|
|
|
@ -1,29 +1,21 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
package ca.uhn.fhir.jpa.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -33,9 +25,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -43,15 +33,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/*
|
||||
/**
|
||||
* This set of integration tests that instantiates and injects an instance of
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
|
||||
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
|
||||
* implementation. It also exercises the code found in
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
||||
* implementation. It also exercises the validateCode output translation code found in
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
|
||||
*/
|
||||
public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeOperationWithRemoteTerminologyR4Test.class);
|
||||
public class ValidateCodeWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeWithRemoteTerminologyR4Test.class);
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]";
|
||||
private static final String CODE_BODY_MASS_INDEX = "39156-5";
|
||||
|
@ -64,8 +54,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
private MyCodeSystemProvider myCodeSystemProvider;
|
||||
private MyValueSetProvider myValueSetProvider;
|
||||
private IValidationProviders.MyValidationProvider<CodeSystem> myCodeSystemProvider;
|
||||
private IValidationProviders.MyValidationProvider<ValueSet> myValueSetProvider;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
|
@ -76,8 +66,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
myValidationSupportChain.addValidationSupport(0, mySvc);
|
||||
myCodeSystemProvider = new MyCodeSystemProvider();
|
||||
myValueSetProvider = new MyValueSetProvider();
|
||||
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
|
||||
myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
|
||||
ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
|
||||
ourRestfulServerExtension.registerProvider(myValueSetProvider);
|
||||
}
|
||||
|
@ -103,11 +93,11 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
||||
myCodeSystemProvider.myReturnParams = new Parameters();
|
||||
myCodeSystemProvider.myReturnParams.addParameter("result", true);
|
||||
myCodeSystemProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||
final String code = "P";
|
||||
final String system = CODE_SYSTEM_V2_0247_URI;;
|
||||
|
||||
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY);
|
||||
setupCodeSystemValidateCode(system, code, params);
|
||||
|
||||
logAllConcepts();
|
||||
|
||||
|
@ -115,8 +105,8 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||
.andParameter("url", new UriType(CODE_SYSTEM_V2_0247_URI))
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(system).setCode(code))
|
||||
.andParameter("url", new UriType(system))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
|
@ -128,7 +118,7 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.setShouldThrowExceptionForResourceNotFound(false);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -166,21 +156,21 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
myValueSetProvider.myReturnParams = new Parameters();
|
||||
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||
final String code = "alerts";
|
||||
final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes";
|
||||
final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes";
|
||||
|
||||
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY);
|
||||
setupValueSetValidateCode(valueSetUrl, system, code, params);
|
||||
setupCodeSystemValidateCode(system, code, params);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "code", new CodeType("alerts"))
|
||||
.andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/list-example-use-codes"))
|
||||
.andParameter("url", new UriType("http://hl7.org/fhir/ValueSet/list-example-codes"))
|
||||
.withParameter(Parameters.class, "code", new CodeType(code))
|
||||
.andParameter("system", new UriType(system))
|
||||
.andParameter("url", new UriType(valueSetUrl))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
||||
|
@ -193,21 +183,20 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnValueSet_byUrlSystemAndCode() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
myValueSetProvider.myReturnParams = new Parameters();
|
||||
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY_BODY_MASS_INDEX);
|
||||
final String code = CODE_BODY_MASS_INDEX;
|
||||
final String system = "http://terminology.hl7.org/CodeSystem/list-example-use-codes";
|
||||
final String valueSetUrl = "http://hl7.org/fhir/ValueSet/list-example-codes";
|
||||
|
||||
Parameters params = new Parameters().addParameter("result", true).addParameter("display", DISPLAY_BODY_MASS_INDEX);
|
||||
setupValueSetValidateCode(valueSetUrl, system, code, params);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "code", new CodeType(CODE_BODY_MASS_INDEX))
|
||||
.andParameter("url", new UriType("https://loinc.org"))
|
||||
.andParameter("system", new UriType("http://loinc.org"))
|
||||
.withParameter(Parameters.class, "code", new CodeType(code))
|
||||
.andParameter("url", new UriType(valueSetUrl))
|
||||
.andParameter("system", new UriType(system))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
|
@ -219,7 +208,7 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
|
||||
@Test
|
||||
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.setShouldThrowExceptionForResourceNotFound(false);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -238,70 +227,18 @@ public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResour
|
|||
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||
private List<CodeSystem> myReturnCodeSystems;
|
||||
private Parameters myReturnParams;
|
||||
private void setupValueSetValidateCode(String theUrl, String theSystem, String theCode, IBaseParameters theResponseParams) {
|
||||
ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl);
|
||||
myValueSetProvider.addTerminologyResource(theSystem);
|
||||
myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, theResponseParams);
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
||||
) {
|
||||
return myReturnParams;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
assert myReturnCodeSystems != null;
|
||||
return myReturnCodeSystems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyValueSetProvider implements IResourceProvider {
|
||||
private Parameters myReturnParams;
|
||||
private List<ValueSet> myReturnValueSets;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||
) {
|
||||
return myReturnParams;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
assert myReturnValueSets != null;
|
||||
return myReturnValueSets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return ValueSet.class;
|
||||
// we currently do this because VersionSpecificWorkerContextWrapper has logic to infer the system when missing
|
||||
// based on the ValueSet by calling ValidationSupportUtils#extractCodeSystemForCode.
|
||||
valueSet.getCompose().addInclude().setSystem(theSystem);
|
||||
}
|
||||
|
||||
private void setupCodeSystemValidateCode(String theUrl, String theCode, IBaseParameters theResponseParams) {
|
||||
CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
|
||||
myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, theResponseParams);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
package ca.uhn.fhir.jpa.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProviders;
|
||||
import ca.uhn.fhir.test.utilities.validation.IValidationProvidersR4;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.Encounter;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Procedure;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_VALIDATE_CODE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests resource validation with Remote Terminology bindings.
|
||||
* To create a new test, you need to do 3 things:
|
||||
* (1) the resource profile, if any custom one is needed should be stored in the FHIR repository
|
||||
* (2) all the CodeSystem and ValueSet terminology resources need to be added to the corresponding resource provider.
|
||||
* At the moment only placeholder CodeSystem/ValueSet resources are returned with id and url populated. For the moment
|
||||
* there was no need to load the full resource, but that can be done if there is logic run which requires it.
|
||||
* This is a minimal setup.
|
||||
* (3) the Remote Terminology operation responses that are needed for the test need to be added to the corresponding
|
||||
* resource provider. The intention is to record and use the responses of an actual terminology server
|
||||
* e.g. <a href="https://r4.ontoserver.csiro.au/fhir/">OntoServer</a>.
|
||||
* This is done as a result of the fact that unit test cannot always catch bugs which are introduced as a result of
|
||||
* changes in the OntoServer or FHIR Validator library, or both.
|
||||
* @see #setupValueSetValidateCode
|
||||
* @see #setupCodeSystemValidateCode
|
||||
* The responses are in Parameters resource format where issues is an OperationOutcome resource.
|
||||
*/
|
||||
public class ValidateWithRemoteTerminologyTest extends BaseResourceProviderR4Test {
|
||||
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||
|
||||
@RegisterExtension
|
||||
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
@Autowired
|
||||
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
private ValidationSupportChain myValidationSupportChain;
|
||||
private IValidationProviders.MyValidationProvider<CodeSystem> myCodeSystemProvider;
|
||||
private IValidationProviders.MyValidationProvider<ValueSet> myValueSetProvider;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
myValidationSupportChain.addValidationSupport(0, mySvc);
|
||||
myCodeSystemProvider = new IValidationProvidersR4.MyCodeSystemProviderR4();
|
||||
myValueSetProvider = new IValidationProvidersR4.MyValueSetProviderR4();
|
||||
ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
|
||||
ourRestfulServerExtension.registerProvider(myValueSetProvider);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
myValidationSupportChain.removeValidationSupport(mySvc);
|
||||
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
ourRestfulServerExtension.unregisterProvider(myCodeSystemProvider);
|
||||
ourRestfulServerExtension.unregisterProvider(myValueSetProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validate_withProfileWithValidCodesFromAllBindingTypes_returnsNoErrors() {
|
||||
// setup
|
||||
final StructureDefinition profileEncounter = ClasspathUtil.loadResource(ourCtx, StructureDefinition.class, "validation/encounter/profile-encounter-custom.json");
|
||||
myClient.update().resource(profileEncounter).execute();
|
||||
|
||||
final String statusCode = "planned";
|
||||
final String classCode = "IMP";
|
||||
final String identifierTypeCode = "VN";
|
||||
|
||||
final String statusSystem = "http://hl7.org/fhir/encounter-status"; // implied system
|
||||
final String classSystem = "http://terminology.hl7.org/CodeSystem/v3-ActCode";
|
||||
final String identifierTypeSystem = "http://terminology.hl7.org/CodeSystem/v2-0203";
|
||||
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/encounter-status", "http://hl7.org/fhir/encounter-status", statusCode, "validation/encounter/validateCode-ValueSet-encounter-status.json");
|
||||
setupValueSetValidateCode("http://terminology.hl7.org/ValueSet/v3-ActEncounterCode", "http://terminology.hl7.org/CodeSystem/v3-ActCode", classCode, "validation/encounter/validateCode-ValueSet-v3-ActEncounterCode.json");
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/identifier-type", "http://hl7.org/fhir/identifier-type", identifierTypeCode, "validation/encounter/validateCode-ValueSet-identifier-type.json");
|
||||
|
||||
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/encounter/validateCode-CodeSystem-encounter-status.json");
|
||||
setupCodeSystemValidateCode(classSystem, classCode, "validation/encounter/validateCode-CodeSystem-v3-ActCode.json");
|
||||
setupCodeSystemValidateCode(identifierTypeSystem, identifierTypeCode, "validation/encounter/validateCode-CodeSystem-v2-0203.json");
|
||||
|
||||
Encounter encounter = new Encounter();
|
||||
encounter.getMeta().addProfile("http://example.ca/fhir/StructureDefinition/profile-encounter");
|
||||
|
||||
// required binding
|
||||
encounter.setStatus(Encounter.EncounterStatus.fromCode(statusCode));
|
||||
|
||||
// preferred binding
|
||||
encounter.getClass_()
|
||||
.setSystem(classSystem)
|
||||
.setCode(classCode)
|
||||
.setDisplay("inpatient encounter");
|
||||
|
||||
// extensible binding
|
||||
encounter.addIdentifier()
|
||||
.getType().addCoding()
|
||||
.setSystem(identifierTypeSystem)
|
||||
.setCode(identifierTypeCode)
|
||||
.setDisplay("Visit number");
|
||||
|
||||
// execute
|
||||
List<String> errors = getValidationErrors(encounter);
|
||||
|
||||
// verify
|
||||
assertThat(errors).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validate_withInvalidCode_returnsErrors() {
|
||||
// setup
|
||||
final String statusCode = "final";
|
||||
final String code = "10xx";
|
||||
|
||||
final String statusSystem = "http://hl7.org/fhir/observation-status";
|
||||
final String loincSystem = "http://loinc.org";
|
||||
final String system = "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM";
|
||||
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-status", statusSystem, statusCode, "validation/observation/validateCode-ValueSet-observation-status.json");
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/observation-codes", loincSystem, statusCode, "validation/observation/validateCode-ValueSet-codes.json");
|
||||
|
||||
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/observation/validateCode-CodeSystem-observation-status.json");
|
||||
setupCodeSystemValidateCode(system, code, "validation/observation/validateCode-CodeSystem-ICD9CM.json");
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.setStatus(Observation.ObservationStatus.fromCode(statusCode));
|
||||
obs.getCode().addCoding().setCode(code).setSystem(system);
|
||||
|
||||
// execute
|
||||
List<String> errors = getValidationErrors(obs);
|
||||
assertThat(errors).hasSize(1);
|
||||
|
||||
// verify
|
||||
assertThat(errors.get(0))
|
||||
.contains("Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validate_withProfileWithInvalidCode_returnsErrors() {
|
||||
// setup
|
||||
String profile = "http://example.ca/fhir/StructureDefinition/profile-procedure";
|
||||
StructureDefinition profileProcedure = ClasspathUtil.loadResource(myFhirContext, StructureDefinition.class, "validation/procedure/profile-procedure.json");
|
||||
myClient.update().resource(profileProcedure).execute();
|
||||
|
||||
final String statusCode = "completed";
|
||||
final String procedureCode1 = "417005";
|
||||
final String procedureCode2 = "xx417005";
|
||||
|
||||
final String statusSystem = "http://hl7.org/fhir/event-status";
|
||||
final String snomedSystem = "http://snomed.info/sct";
|
||||
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode1, "validation/procedure/validateCode-ValueSet-procedure-code-valid.json");
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode2, "validation/procedure/validateCode-ValueSet-procedure-code-invalid.json");
|
||||
|
||||
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/procedure/validateCode-CodeSystem-event-status.json");
|
||||
setupCodeSystemValidateCode(snomedSystem, procedureCode1, "validation/procedure/validateCode-CodeSystem-snomed-valid.json");
|
||||
setupCodeSystemValidateCode(snomedSystem, procedureCode2, "validation/procedure/validateCode-CodeSystem-snomed-invalid.json");
|
||||
|
||||
Procedure procedure = new Procedure();
|
||||
procedure.setSubject(new Reference("Patient/P1"));
|
||||
procedure.setStatus(Procedure.ProcedureStatus.fromCode(statusCode));
|
||||
procedure.getCode().addCoding().setSystem(snomedSystem).setCode(procedureCode1);
|
||||
procedure.getCode().addCoding().setSystem(snomedSystem).setCode(procedureCode2);
|
||||
procedure.getMeta().addProfile(profile);
|
||||
|
||||
// execute
|
||||
List<String> errors = getValidationErrors(procedure);
|
||||
// TODO: there is currently some duplication in the errors returned. This needs to be investigated and fixed.
|
||||
// assertThat(errors).hasSize(1);
|
||||
|
||||
// verify
|
||||
// note that we're not selecting an explicit versions (using latest) so the message verification does not include it.
|
||||
assertThat(StringUtils.join("", errors))
|
||||
.contains("Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct'")
|
||||
.doesNotContain("The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code")
|
||||
.doesNotContain("http://snomed.info/sct#417005");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validate_withProfileWithSlicingWithValidCode_returnsNoErrors() {
|
||||
// setup
|
||||
String profile = "http://example.ca/fhir/StructureDefinition/profile-procedure-with-slicing";
|
||||
StructureDefinition profileProcedure = ClasspathUtil.loadResource(myFhirContext, StructureDefinition.class, "validation/procedure/profile-procedure-slicing.json");
|
||||
myClient.update().resource(profileProcedure).execute();
|
||||
|
||||
final String statusCode = "completed";
|
||||
final String procedureCode = "no-procedure-info";
|
||||
|
||||
final String statusSystem = "http://hl7.org/fhir/event-status";
|
||||
final String snomedSystem = "http://snomed.info/sct";
|
||||
final String absentUnknownSystem = "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips";
|
||||
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/event-status", statusSystem, statusCode, "validation/procedure/validateCode-ValueSet-event-status.json");
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/ValueSet/procedure-code", snomedSystem, procedureCode, "validation/procedure/validateCode-ValueSet-procedure-code-invalid-slice.json");
|
||||
setupValueSetValidateCode("http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips", absentUnknownSystem, procedureCode, "validation/procedure/validateCode-ValueSet-absent-or-unknown-procedure.json");
|
||||
|
||||
setupCodeSystemValidateCode(statusSystem, statusCode, "validation/procedure/validateCode-CodeSystem-event-status.json");
|
||||
setupCodeSystemValidateCode(absentUnknownSystem, procedureCode, "validation/procedure/validateCode-CodeSystem-absent-or-unknown.json");
|
||||
|
||||
Procedure procedure = new Procedure();
|
||||
procedure.setSubject(new Reference("Patient/P1"));
|
||||
procedure.setStatus(Procedure.ProcedureStatus.fromCode(statusCode));
|
||||
procedure.getCode().addCoding().setSystem(absentUnknownSystem).setCode(procedureCode);
|
||||
procedure.getMeta().addProfile(profile);
|
||||
|
||||
// execute
|
||||
List<String> errors = getValidationErrors(procedure);
|
||||
assertThat(errors).hasSize(0);
|
||||
}
|
||||
|
||||
private void setupValueSetValidateCode(String theUrl, String theSystem, String theCode, String theTerminologyResponseFile) {
|
||||
ValueSet valueSet = myValueSetProvider.addTerminologyResource(theUrl);
|
||||
myCodeSystemProvider.addTerminologyResource(theSystem);
|
||||
myValueSetProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, valueSet.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
|
||||
|
||||
// we currently do this because VersionSpecificWorkerContextWrapper has logic to infer the system when missing
|
||||
// based on the ValueSet by calling ValidationSupportUtils#extractCodeSystemForCode.
|
||||
valueSet.getCompose().addInclude().setSystem(theSystem);
|
||||
|
||||
// you will notice each of these calls require also a call to setupCodeSystemValidateCode
|
||||
// that is necessary because VersionSpecificWorkerContextWrapper#validateCodeInValueSet
|
||||
// which also attempts a validateCode against the CodeSystem after the validateCode against the ValueSet
|
||||
}
|
||||
|
||||
private void setupCodeSystemValidateCode(String theUrl, String theCode, String theTerminologyResponseFile) {
|
||||
CodeSystem codeSystem = myCodeSystemProvider.addTerminologyResource(theUrl);
|
||||
myCodeSystemProvider.addTerminologyResponse(OPERATION_VALIDATE_CODE, codeSystem.getUrl(), theCode, ourCtx, theTerminologyResponseFile);
|
||||
}
|
||||
|
||||
private List<String> getValidationErrors(IBaseResource theResource) {
|
||||
MethodOutcome resultProcedure = myClient.validate().resource(theResource).execute();
|
||||
OperationOutcome operationOutcome = (OperationOutcome) resultProcedure.getOperationOutcome();
|
||||
return operationOutcome.getIssue().stream()
|
||||
.filter(issue -> issue.getSeverity() == OperationOutcome.IssueSeverity.ERROR)
|
||||
.map(OperationOutcome.OperationOutcomeIssueComponent::getDiagnostics)
|
||||
.toList();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"resourceType": "StructureDefinition",
|
||||
"id": "profile-encounter",
|
||||
"url": "http://example.ca/fhir/StructureDefinition/profile-encounter",
|
||||
"version": "0.11.0",
|
||||
"name": "EncounterProfile",
|
||||
"title": "Encounter Profile",
|
||||
"status": "active",
|
||||
"date": "2022-10-15T12:00:00+00:00",
|
||||
"publisher": "Example Organization",
|
||||
"fhirVersion": "4.0.1",
|
||||
"kind": "resource",
|
||||
"abstract": false,
|
||||
"type": "Encounter",
|
||||
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Encounter",
|
||||
"derivation": "constraint",
|
||||
"differential": {
|
||||
"element": [
|
||||
{
|
||||
"id": "Encounter.identifier.type.coding",
|
||||
"path": "Encounter.identifier.type.coding",
|
||||
"min": 1,
|
||||
"max": "1",
|
||||
"mustSupport": true
|
||||
},
|
||||
{
|
||||
"id": "Encounter.identifier.type.coding.system",
|
||||
"path": "Encounter.identifier.type.coding.system",
|
||||
"min": 1,
|
||||
"fixedUri": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"mustSupport": true
|
||||
},
|
||||
{
|
||||
"id": "Encounter.identifier.type.coding.code",
|
||||
"path": "Encounter.identifier.type.coding.code",
|
||||
"min": 1,
|
||||
"fixedCode": "VN",
|
||||
"mustSupport": true
|
||||
},
|
||||
{
|
||||
"id": "Encounter.identifier.type.coding.display",
|
||||
"path": "Encounter.identifier.type.coding.display",
|
||||
"min": 1,
|
||||
"fixedString": "Visit number",
|
||||
"mustSupport": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "planned"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/encounter-status"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "5.0.0-ballot"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Planned"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to draft CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "VN"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://terminology.hl7.org/CodeSystem/v2-0203"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "3.0.0"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Visit number"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "IMP"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://terminology.hl7.org/CodeSystem/v3-ActCode"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "2018-08-12"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "inpatient encounter"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to draft CodeSystem http://terminology.hl7.org/CodeSystem/v3-ActCode|2018-08-12"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "planned"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/encounter-status"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "5.0.0-ballot"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Planned"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to draft CodeSystem http://hl7.org/fhir/encounter-status|5.0.0-ballot"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": false
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "VN"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://terminology.hl7.org/CodeSystem/v2-0203"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "3.0.0"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "code-invalid",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "not-in-vs"
|
||||
}
|
||||
],
|
||||
"text": "The provided code 'http://terminology.hl7.org/CodeSystem/v2-0203#VN' was not found in the value set 'http://hl7.org/fhir/ValueSet/identifier-type|5.0.0-ballot'"
|
||||
},
|
||||
"location": [
|
||||
"code"
|
||||
],
|
||||
"expression": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"valueString": "The provided code 'http://terminology.hl7.org/CodeSystem/v2-0203#VN' was not found in the value set 'http://hl7.org/fhir/ValueSet/identifier-type|5.0.0-ballot'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "IMP"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://terminology.hl7.org/CodeSystem/v3-ActCode"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "2018-08-12"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "inpatient encounter"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to trial-use ValueSet http://terminology.hl7.org/ValueSet/v3-ActEncounterCode|2014-03-26"
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to draft CodeSystem http://terminology.hl7.org/CodeSystem/v3-ActCode|2018-08-12"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": false
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "10xx"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "code-invalid",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "invalid-code"
|
||||
}
|
||||
],
|
||||
"text": "Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM' version '0.1.0'"
|
||||
},
|
||||
"location": [
|
||||
"code"
|
||||
],
|
||||
"expression": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"valueString": "Unknown code '10xx' in the CodeSystem 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM' version '0.1.0'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "final"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/observation-status"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "5.0.0-ballot"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Final"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": false
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "10xx"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "code-invalid",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "not-in-vs"
|
||||
}
|
||||
],
|
||||
"text": "The provided code 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM#10xx' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-codes|5.0.0-ballot'"
|
||||
},
|
||||
"location": [
|
||||
"code"
|
||||
],
|
||||
"expression": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"valueString": "The provided code 'http://fhir.infoway-inforoute.ca/io/psca/CodeSystem/ICD9CM#10xx' was not found in the value set 'http://hl7.org/fhir/ValueSet/observation-codes|5.0.0-ballot'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "final"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/observation-status"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "5.0.0-ballot"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Final"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"resourceType": "StructureDefinition",
|
||||
"id": "profile-procedure-with-slicing",
|
||||
"url": "http://example.ca/fhir/StructureDefinition/profile-procedure-with-slicing",
|
||||
"version": "0.11.0",
|
||||
"name": "ProcedureProfile",
|
||||
"title": "Procedure Profile",
|
||||
"status": "active",
|
||||
"date": "2022-10-15T12:00:00+00:00",
|
||||
"publisher": "Example Organization",
|
||||
"fhirVersion": "4.0.1",
|
||||
"kind": "resource",
|
||||
"abstract": false,
|
||||
"type": "Procedure",
|
||||
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Procedure",
|
||||
"derivation": "constraint",
|
||||
"differential": {
|
||||
"element": [
|
||||
{
|
||||
"id": "Procedure.code.coding",
|
||||
"path": "Procedure.code.coding",
|
||||
"slicing": {
|
||||
"discriminator": [
|
||||
{
|
||||
"type": "pattern",
|
||||
"path": "$this"
|
||||
}
|
||||
],
|
||||
"description": "Discriminated by the bound value set",
|
||||
"rules": "open"
|
||||
},
|
||||
"mustSupport": true,
|
||||
"binding": {
|
||||
"strength": "preferred",
|
||||
"valueSet": "http://hl7.org/fhir/ValueSet/procedure-code"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "Procedure.code.coding.display.extension:translation",
|
||||
"path": "Procedure.code.coding.display.extension",
|
||||
"sliceName": "translation"
|
||||
},
|
||||
{
|
||||
"id": "Procedure.code.coding.display.extension:translation.extension",
|
||||
"path": "Procedure.code.coding.display.extension.extension",
|
||||
"min": 2
|
||||
},
|
||||
{
|
||||
"id": "Procedure.code.coding:absentOrUnknownProcedure",
|
||||
"path": "Procedure.code.coding",
|
||||
"sliceName": "absentOrUnknownProcedure",
|
||||
"short": "Optional slice for representing a code for absent problem or for unknown procedure",
|
||||
"definition": "Code representing the statement \"absent problem\" or the statement \"procedures unknown\"",
|
||||
"mustSupport": true,
|
||||
"binding": {
|
||||
"extension": [
|
||||
{
|
||||
"url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName",
|
||||
"valueString": "absentOrUnknownProcedure"
|
||||
}
|
||||
],
|
||||
"strength": "required",
|
||||
"description": "A code to identify absent or unknown procedures",
|
||||
"valueSet": "http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "Procedure.code.coding:absentOrUnknownProcedure.display.extension:translation",
|
||||
"path": "Procedure.code.coding.display.extension",
|
||||
"sliceName": "translation"
|
||||
},
|
||||
{
|
||||
"id": "Procedure.code.coding:absentOrUnknownProcedure.display.extension:translation.extension",
|
||||
"path": "Procedure.code.coding.display.extension.extension",
|
||||
"min": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"resourceType": "StructureDefinition",
|
||||
"id": "profile-procedure",
|
||||
"url": "http://example.ca/fhir/StructureDefinition/profile-procedure",
|
||||
"version": "0.11.0",
|
||||
"name": "ProcedureProfile",
|
||||
"title": "Procedure Profile",
|
||||
"status": "active",
|
||||
"date": "2022-10-15T12:00:00+00:00",
|
||||
"publisher": "Example Organization",
|
||||
"fhirVersion": "4.0.1",
|
||||
"kind": "resource",
|
||||
"abstract": false,
|
||||
"type": "Procedure",
|
||||
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Procedure",
|
||||
"derivation": "constraint",
|
||||
"differential": {
|
||||
"element": [
|
||||
{
|
||||
"id": "Procedure.code.coding",
|
||||
"path": "Procedure.code.coding",
|
||||
"slicing": {
|
||||
"discriminator": [
|
||||
{
|
||||
"type": "pattern",
|
||||
"path": "$this"
|
||||
}
|
||||
],
|
||||
"description": "Discriminated by the bound value set",
|
||||
"rules": "open"
|
||||
},
|
||||
"mustSupport": true,
|
||||
"binding": {
|
||||
"strength": "preferred",
|
||||
"valueSet": "http://hl7.org/fhir/ValueSet/procedure-code"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "Procedure.code.coding.display.extension:translation",
|
||||
"path": "Procedure.code.coding.display.extension",
|
||||
"sliceName": "translation"
|
||||
},
|
||||
{
|
||||
"id": "Procedure.code.coding.display.extension:translation.extension",
|
||||
"path": "Procedure.code.coding.display.extension.extension",
|
||||
"min": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "no-procedure-info"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "1.1.0"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "No information about past history of procedures"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips|1.1.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "completed"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/event-status"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "5.0.0-ballot"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Completed"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/event-status|5.0.0-ballot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to experimental CodeSystem http://hl7.org/fhir/event-status|5.0.0-ballot"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": false
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "xx417005"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://snomed.info/sct"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "code-invalid",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "invalid-code"
|
||||
}
|
||||
],
|
||||
"text": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
|
||||
},
|
||||
"location": [
|
||||
"code"
|
||||
],
|
||||
"expression": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"valueString": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "417005"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://snomed.info/sct"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "http://snomed.info/sct/32506021000036107/version/20241031"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Hospital re-admission"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "no-procedure-info"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "1.1.0"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "No information about past history of procedures"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to trial-use CodeSystem http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips|1.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to trial-use ValueSet http://hl7.org/fhir/uv/ips/ValueSet/absent-or-unknown-procedures-uv-ips|1.1.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "final"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/procedure-status"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "5.0.0-ballot"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Final"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": false
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "no-procedure-info"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "code-invalid",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "not-in-vs"
|
||||
}
|
||||
],
|
||||
"text": "The provided code 'http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips#no-procedure-info' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
|
||||
},
|
||||
"location": [
|
||||
"code"
|
||||
],
|
||||
"expression": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"valueString": "The provided code 'http://hl7.org/fhir/uv/ips/CodeSystem/absent-unknown-uv-ips#no-procedure-info' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": false
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "xx417005"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://snomed.info/sct"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "code-invalid",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "not-in-vs"
|
||||
}
|
||||
],
|
||||
"text": "The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
|
||||
},
|
||||
"location": [
|
||||
"code"
|
||||
],
|
||||
"expression": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "code-invalid",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "invalid-code"
|
||||
}
|
||||
],
|
||||
"text": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'"
|
||||
},
|
||||
"location": [
|
||||
"code"
|
||||
],
|
||||
"expression": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "message",
|
||||
"valueString": "Unknown code 'xx417005' in the CodeSystem 'http://snomed.info/sct' version 'http://snomed.info/sct/32506021000036107/version/20241031'; The provided code 'http://snomed.info/sct#xx417005' was not found in the value set 'http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot'"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "result",
|
||||
"valueBoolean": true
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "417005"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"valueUri": "http://snomed.info/sct"
|
||||
},
|
||||
{
|
||||
"name": "version",
|
||||
"valueString": "http://snomed.info/sct/32506021000036107/version/20241031"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Hospital re-admission"
|
||||
},
|
||||
{
|
||||
"name": "issues",
|
||||
"resource": {
|
||||
"resourceType": "OperationOutcome",
|
||||
"issue": [
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to draft ValueSet http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot"
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "information",
|
||||
"code": "business-rule",
|
||||
"details": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||
"code": "status-check"
|
||||
}
|
||||
],
|
||||
"text": "Reference to experimental ValueSet http://hl7.org/fhir/ValueSet/procedure-code|5.0.0-ballot"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -51,3 +51,33 @@ INSERT INTO TRM_CONCEPT_DESIG (
|
|||
54,
|
||||
150
|
||||
);
|
||||
|
||||
INSERT INTO HFJ_RES_LINK (
|
||||
PID,
|
||||
PARTITION_DATE,
|
||||
PARTITION_ID,
|
||||
SRC_PATH,
|
||||
SRC_RESOURCE_ID,
|
||||
SOURCE_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_ID,
|
||||
TARGET_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_URL,
|
||||
TARGET_RESOURCE_VERSION,
|
||||
SP_UPDATED,
|
||||
TARGET_RES_PARTITION_ID,
|
||||
TARGET_RES_PARTITION_DATE
|
||||
) VALUES (
|
||||
702,
|
||||
'2024-11-05',
|
||||
1,
|
||||
'Observation.subject.where(resolve() is Patient)',
|
||||
1656,
|
||||
'Observation',
|
||||
1906,
|
||||
'Patient',
|
||||
'http://localhost:8000/Patient/123',
|
||||
1,
|
||||
'2024-11-01 18:01:12.921',
|
||||
1,
|
||||
'2024-11-05'
|
||||
);
|
||||
|
|
|
@ -51,3 +51,33 @@ INSERT INTO TRM_CONCEPT_DESIG (
|
|||
54,
|
||||
150
|
||||
);
|
||||
|
||||
INSERT INTO HFJ_RES_LINK (
|
||||
PID,
|
||||
PARTITION_DATE,
|
||||
PARTITION_ID,
|
||||
SRC_PATH,
|
||||
SRC_RESOURCE_ID,
|
||||
SOURCE_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_ID,
|
||||
TARGET_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_URL,
|
||||
TARGET_RESOURCE_VERSION,
|
||||
SP_UPDATED,
|
||||
TARGET_RES_PARTITION_ID,
|
||||
TARGET_RES_PARTITION_DATE
|
||||
) VALUES (
|
||||
702,
|
||||
'2024-11-05',
|
||||
1,
|
||||
'Observation.subject.where(resolve() is Patient)',
|
||||
1653,
|
||||
'Observation',
|
||||
1906,
|
||||
'Patient',
|
||||
'http://localhost:8000/Patient/123',
|
||||
1,
|
||||
'2024-11-01 18:01:12.921',
|
||||
1,
|
||||
'2024-11-05'
|
||||
);
|
||||
|
|
|
@ -51,3 +51,33 @@ INSERT INTO TRM_CONCEPT_DESIG (
|
|||
54,
|
||||
150
|
||||
);
|
||||
|
||||
INSERT INTO HFJ_RES_LINK (
|
||||
PID,
|
||||
PARTITION_DATE,
|
||||
PARTITION_ID,
|
||||
SRC_PATH,
|
||||
SRC_RESOURCE_ID,
|
||||
SOURCE_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_ID,
|
||||
TARGET_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_URL,
|
||||
TARGET_RESOURCE_VERSION,
|
||||
SP_UPDATED,
|
||||
TARGET_RES_PARTITION_ID,
|
||||
TARGET_RES_PARTITION_DATE
|
||||
) VALUES (
|
||||
702,
|
||||
SYSDATE,
|
||||
1,
|
||||
'Observation.subject.where(resolve() is Patient)',
|
||||
1653,
|
||||
'Observation',
|
||||
1906,
|
||||
'Patient',
|
||||
'http://localhost:8000/Patient/123',
|
||||
1,
|
||||
SYSDATE,
|
||||
1,
|
||||
SYSDATE
|
||||
);
|
||||
|
|
|
@ -51,3 +51,33 @@ INSERT INTO TRM_CONCEPT_DESIG (
|
|||
54,
|
||||
150
|
||||
);
|
||||
|
||||
INSERT INTO HFJ_RES_LINK (
|
||||
PID,
|
||||
PARTITION_DATE,
|
||||
PARTITION_ID,
|
||||
SRC_PATH,
|
||||
SRC_RESOURCE_ID,
|
||||
SOURCE_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_ID,
|
||||
TARGET_RESOURCE_TYPE,
|
||||
TARGET_RESOURCE_URL,
|
||||
TARGET_RESOURCE_VERSION,
|
||||
SP_UPDATED,
|
||||
TARGET_RES_PARTITION_ID,
|
||||
TARGET_RES_PARTITION_DATE
|
||||
) VALUES (
|
||||
702,
|
||||
'2024-11-05',
|
||||
1,
|
||||
'Observation.subject.where(resolve() is Patient)',
|
||||
1653,
|
||||
'Observation',
|
||||
1906,
|
||||
'Patient',
|
||||
'http://localhost:8000/Patient/123',
|
||||
1,
|
||||
'2024-11-01 18:01:12.921',
|
||||
1,
|
||||
'2024-11-05'
|
||||
);
|
||||
|
|
|
@ -81,7 +81,7 @@ public class CompositeInterceptorBroadcaster {
|
|||
}
|
||||
if (theRequestDetails != null && theRequestDetails.getInterceptorBroadcaster() != null && retVal) {
|
||||
IInterceptorBroadcaster interceptorBroadcaster = theRequestDetails.getInterceptorBroadcaster();
|
||||
interceptorBroadcaster.callHooks(thePointcut, theParams);
|
||||
retVal = interceptorBroadcaster.callHooks(thePointcut, theParams);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
package ca.uhn.fhir.rest.server.util;
|
||||
|
||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class CompositeInterceptorBroadcasterTest {
|
||||
|
||||
@Mock
|
||||
private IInterceptorBroadcaster myModuleBroadcasterMock;
|
||||
@Mock
|
||||
private IInterceptorBroadcaster myReqDetailsBroadcasterMock;
|
||||
@Mock
|
||||
private Pointcut myPointcutMock;
|
||||
@Mock
|
||||
private HookParams myHookParamsMock;
|
||||
@Mock
|
||||
private RequestDetails myRequestDetailsMock;
|
||||
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsTrue_ThenReturnsTrue() {
|
||||
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
|
||||
|
||||
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
|
||||
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock,
|
||||
myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isTrue();
|
||||
|
||||
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_RequestDetailsBroadcasterReturnsFalse_ThenReturnsFalse() {
|
||||
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
|
||||
|
||||
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
|
||||
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock,
|
||||
myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isFalse();
|
||||
|
||||
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenModuleBroadcasterReturnsFalse_ThenSkipsBroadcasterInRequestDetails_And_ReturnsFalse() {
|
||||
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
|
||||
|
||||
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock,
|
||||
myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isFalse();
|
||||
|
||||
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
verify(myReqDetailsBroadcasterMock, never()).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetailsBroadcaster_ThenReturnsTrue() {
|
||||
|
||||
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
|
||||
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(null);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, myPointcutMock,
|
||||
myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isTrue();
|
||||
|
||||
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetailsBroadcaster_ThenReturnsFalse() {
|
||||
|
||||
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
|
||||
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(null);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, myRequestDetailsMock, myPointcutMock,
|
||||
myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isFalse();
|
||||
|
||||
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenModuleBroadcasterReturnsTrue_And_NullRequestDetails_ThenReturnsTrue() {
|
||||
|
||||
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, null, myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isTrue();
|
||||
|
||||
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenModuleBroadcasterReturnsFalse_And_NullRequestDetails_ThenReturnsFalse() {
|
||||
|
||||
when(myModuleBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(myModuleBroadcasterMock, null, myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isFalse();
|
||||
|
||||
verify(myModuleBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenNullModuleBroadcaster_And_NullRequestDetails_ThenReturnsTrue() {
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, null, myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenNullModuleBroadcaster_And_RequestDetailsBroadcasterReturnsTrue_ThenReturnsTrue() {
|
||||
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
|
||||
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(true);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, myRequestDetailsMock, myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isTrue();
|
||||
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void doCallHooks_WhenNullModuleBroadcaster_And_RequestDetailsBroadcasterReturnsFalse_ThenReturnsFalse() {
|
||||
when(myRequestDetailsMock.getInterceptorBroadcaster()).thenReturn(myReqDetailsBroadcasterMock);
|
||||
when(myReqDetailsBroadcasterMock.callHooks(myPointcutMock, myHookParamsMock)).thenReturn(false);
|
||||
|
||||
boolean retVal = CompositeInterceptorBroadcaster.doCallHooks(null, myRequestDetailsMock, myPointcutMock, myHookParamsMock);
|
||||
|
||||
assertThat(retVal).isFalse();
|
||||
verify(myReqDetailsBroadcasterMock).callHooks(myPointcutMock, myHookParamsMock);
|
||||
}
|
||||
}
|
|
@ -397,7 +397,7 @@ public class Builder {
|
|||
private final String myVersion;
|
||||
private final boolean myUnique;
|
||||
private String[] myIncludeColumns;
|
||||
private boolean myOnline;
|
||||
private boolean myOnline = true;
|
||||
|
||||
public BuilderAddIndexUnique(String theVersion, boolean theUnique) {
|
||||
myVersion = theVersion;
|
||||
|
|
|
@ -92,7 +92,7 @@ public class ReindexV1Config {
|
|||
"Load IDs of resources to reindex",
|
||||
ResourceIdListWorkChunkJson.class,
|
||||
myReindexLoadIdsStep)
|
||||
.addLastStep("reindex-start", "Start the resource reindex", reindexStepV1())
|
||||
.addLastStep("reindex", "Start the resource reindex", reindexStepV1())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public class BatchWorkChunkStatusDTO {
|
|||
public final WorkChunkStatusEnum status;
|
||||
public final Date start;
|
||||
public final Date stop;
|
||||
|
||||
public final Double avg;
|
||||
public final Long totalChunks;
|
||||
|
||||
|
|
|
@ -21,15 +21,20 @@ package ca.uhn.fhir.cr.config.dstu3;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.cr.config.CrProcessorConfig;
|
||||
import ca.uhn.fhir.cr.config.ProviderLoader;
|
||||
import ca.uhn.fhir.cr.config.ProviderSelector;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
@Import(CrProcessorConfig.class)
|
||||
public class EvaluateOperationConfig {
|
||||
@Bean
|
||||
ca.uhn.fhir.cr.dstu3.library.LibraryEvaluateProvider dstu3LibraryEvaluateProvider() {
|
||||
|
|
|
@ -21,15 +21,20 @@ package ca.uhn.fhir.cr.config.r4;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.cr.config.CrProcessorConfig;
|
||||
import ca.uhn.fhir.cr.config.ProviderLoader;
|
||||
import ca.uhn.fhir.cr.config.ProviderSelector;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
@Configuration
|
||||
@Import(CrProcessorConfig.class)
|
||||
public class EvaluateOperationConfig {
|
||||
@Bean
|
||||
ca.uhn.fhir.cr.r4.library.LibraryEvaluateProvider r4LibraryEvaluateProvider() {
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -54,6 +55,25 @@ public class HapiFhirRepositoryR4Test extends BaseCrR4TestServer {
|
|||
assertTrue(crudTest(repository));
|
||||
}
|
||||
|
||||
@Test
|
||||
void _profileCanBeReferenceParam() {
|
||||
// as per https://www.hl7.org/fhir/r4/search.html#all _profile is a reference param
|
||||
var repository = new HapiFhirRepository(myDaoRegistry, setupRequestDetails(), myRestfulServer);
|
||||
var profileToFind = "http://www.a-test-profile.com";
|
||||
var encounterWithProfile = new Encounter();
|
||||
encounterWithProfile.getMeta().addProfile(profileToFind);
|
||||
repository.create(encounterWithProfile);
|
||||
repository.create(new Encounter());
|
||||
Map<String, List<IQueryParameterType>> map = new HashMap<>();
|
||||
map.put("_profile", Collections.singletonList(new ReferenceParam(profileToFind)));
|
||||
assertDoesNotThrow(() -> {
|
||||
var returnBundle = repository.search(Bundle.class, Encounter.class, map);
|
||||
assertTrue(returnBundle.hasEntry());
|
||||
assertEquals(1,returnBundle.getEntry().size());
|
||||
assertEquals(profileToFind, returnBundle.getEntryFirstRep().getResource().getMeta().getProfile().get(0).getValue());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Boolean crudTest(HapiFhirRepository theRepository) {
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
|||
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||
import ca.uhn.fhir.parser.PatientWithExtendedContactDstu3.CustomContactComponent;
|
||||
import ca.uhn.fhir.parser.XmlParserDstu2_1Test.TestPatientFor327;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -407,6 +408,8 @@ public class JsonParserDstu2_1Test {
|
|||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).containsSubsequence(
|
||||
|
@ -415,14 +418,14 @@ public class JsonParserDstu2_1Test {
|
|||
"\"contained\": [",
|
||||
"{",
|
||||
"\"resourceType\": \"Condition\",",
|
||||
"\"id\": \"1\"",
|
||||
"\"id\": \"" + conditionUuid + "\"",
|
||||
"}",
|
||||
"],",
|
||||
"\"extension\": [",
|
||||
"{",
|
||||
"\"url\": \"test\",",
|
||||
"\"valueReference\": {",
|
||||
"\"reference\": \"#1\"",
|
||||
"\"reference\": \"#" + conditionUuid + "\"",
|
||||
"}",
|
||||
"}",
|
||||
"],",
|
||||
|
@ -632,19 +635,21 @@ public class JsonParserDstu2_1Test {
|
|||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).containsSubsequence(
|
||||
"\"resourceType\": \"Patient\"",
|
||||
"\"contained\": [",
|
||||
"\"resourceType\": \"Condition\"",
|
||||
"\"id\": \"1\"",
|
||||
"\"id\": \"" + conditionUuid + "\"",
|
||||
"\"bodySite\": [",
|
||||
"\"text\": \"BODY SITE\"",
|
||||
"\"extension\": [",
|
||||
"\"url\": \"testCondition\",",
|
||||
"\"valueReference\": {",
|
||||
"\"reference\": \"#1\"",
|
||||
"\"reference\": \"#" + conditionUuid + "\"",
|
||||
"\"birthDate\": \"2016-04-14\"",
|
||||
"}"
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@ import ca.uhn.fhir.parser.FooMessageHeaderWithExplicitField.FooMessageSourceComp
|
|||
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||
import ca.uhn.fhir.parser.PatientWithCustomCompositeExtension.FooParentExtension;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -378,8 +379,11 @@ public class XmlParserDstu2_1Test {
|
|||
|
||||
String encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String organizationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuid);
|
||||
|
||||
assertThat(encoded).contains("<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// Create a bundle with just the patient resource
|
||||
Bundle b = new Bundle();
|
||||
|
@ -388,35 +392,35 @@ public class XmlParserDstu2_1Test {
|
|||
// Encode the bundle
|
||||
encoded = xmlParser.encodeResourceToString(b);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<id value=\"1\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<id value=\"" + organizationUuid + "\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<entry>", "</entry>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<entry>.*</entry>.*<entry>");
|
||||
|
||||
// Re-parse the bundle
|
||||
patient = (Patient) xmlParser.parseResource(xmlParser.encodeResourceToString(patient));
|
||||
assertEquals("#1", patient.getManagingOrganization().getReference());
|
||||
assertEquals("#" + organizationUuid, patient.getManagingOrganization().getReference());
|
||||
|
||||
assertNotNull(patient.getManagingOrganization().getResource());
|
||||
org = (Organization) patient.getManagingOrganization().getResource();
|
||||
assertEquals("#1", org.getIdElement().getValue());
|
||||
assertEquals("#" + organizationUuid, org.getIdElement().getValue());
|
||||
assertEquals("Contained Test Organization", org.getName());
|
||||
|
||||
// And re-encode a second time
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>", "</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"" + organizationUuid + "\"/>", "</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared
|
||||
patient.getContained().clear();
|
||||
patient.getManagingOrganization().setReference(null);
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>", "</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"" + organizationUuid + "\"/>", "</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared and a manually set local ID
|
||||
patient.getContained().clear();
|
||||
|
@ -447,6 +451,8 @@ public class XmlParserDstu2_1Test {
|
|||
|
||||
String output = parser.encodeResourceToString(dr);
|
||||
ourLog.info(output);
|
||||
String observationUuid = UuidUtils.findFirstUUID(output);
|
||||
assertNotNull(observationUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(output).containsSubsequence(
|
||||
|
@ -456,7 +462,7 @@ public class XmlParserDstu2_1Test {
|
|||
"</meta>",
|
||||
"<contained>",
|
||||
"<Observation xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + observationUuid + "\"/>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://custom_Observation\"/>",
|
||||
"</meta>",
|
||||
|
@ -465,7 +471,7 @@ public class XmlParserDstu2_1Test {
|
|||
"</contained>",
|
||||
"<status value=\"final\"/>",
|
||||
"<result>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + observationUuid + "\"/>",
|
||||
"</result>",
|
||||
"</DiagnosticReport>");
|
||||
//@formatter:on
|
||||
|
@ -477,7 +483,7 @@ public class XmlParserDstu2_1Test {
|
|||
dr = (CustomDiagnosticReport) parser.parseResource(output);
|
||||
assertEquals(DiagnosticReportStatus.FINAL, dr.getStatus());
|
||||
|
||||
assertEquals("#1", dr.getResult().get(0).getReference());
|
||||
assertEquals("#" + observationUuid, dr.getResult().get(0).getReference());
|
||||
obs = (CustomObservation) dr.getResult().get(0).getResource();
|
||||
assertEquals(ObservationStatus.FINAL, obs.getStatus());
|
||||
|
||||
|
@ -500,19 +506,21 @@ public class XmlParserDstu2_1Test {
|
|||
|
||||
String output = parser.encodeResourceToString(dr);
|
||||
ourLog.info(output);
|
||||
String observationUuid = UuidUtils.findFirstUUID(output);
|
||||
assertNotNull(observationUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(output).containsSubsequence(
|
||||
"<DiagnosticReport xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Observation xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + observationUuid + "\"/>",
|
||||
"<status value=\"final\"/>",
|
||||
"</Observation>",
|
||||
"</contained>",
|
||||
"<status value=\"final\"/>",
|
||||
"<result>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + observationUuid + "\"/>",
|
||||
"</result>",
|
||||
"</DiagnosticReport>");
|
||||
//@formatter:on
|
||||
|
@ -524,7 +532,7 @@ public class XmlParserDstu2_1Test {
|
|||
dr = (DiagnosticReport) parser.parseResource(output);
|
||||
assertEquals(DiagnosticReportStatus.FINAL, dr.getStatus());
|
||||
|
||||
assertEquals("#1", dr.getResult().get(0).getReference());
|
||||
assertEquals("#" + observationUuid, dr.getResult().get(0).getReference());
|
||||
obs = (Observation) dr.getResult().get(0).getResource();
|
||||
assertEquals(ObservationStatus.FINAL, obs.getStatus());
|
||||
|
||||
|
@ -832,18 +840,20 @@ public class XmlParserDstu2_1Test {
|
|||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).containsSubsequence(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Condition xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + conditionUuid + "\"/>",
|
||||
"</Condition>",
|
||||
"</contained>",
|
||||
"<extension url=\"test\">",
|
||||
"<valueReference>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + conditionUuid + "\"/>",
|
||||
"</valueReference>",
|
||||
"</extension>",
|
||||
"<birthDate value=\"2016-04-05\"/>",
|
||||
|
@ -911,10 +921,12 @@ public class XmlParserDstu2_1Test {
|
|||
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
String encoded = p.encodeResourceToString(medicationPrescript);
|
||||
ourLog.info(encoded);
|
||||
String medicationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(medicationUuid);
|
||||
|
||||
//@formatter:on
|
||||
assertThat(encoded).containsSubsequence("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"1\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#1\"/>",
|
||||
assertThat(encoded).containsSubsequence("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"" + medicationUuid + "\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#" + medicationUuid + "\"/>",
|
||||
"<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>");
|
||||
//@formatter:off
|
||||
}
|
||||
|
@ -1185,13 +1197,15 @@ public class XmlParserDstu2_1Test {
|
|||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).containsSubsequence(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Condition xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + conditionUuid + "\"/>",
|
||||
"<bodySite>",
|
||||
"<text value=\"BODY SITE\"/>",
|
||||
"</bodySite>",
|
||||
|
@ -1199,7 +1213,7 @@ public class XmlParserDstu2_1Test {
|
|||
"</contained>",
|
||||
"<extension url=\"testCondition\">",
|
||||
"<valueReference>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + conditionUuid + "\"/>",
|
||||
"</valueReference>",
|
||||
"</extension>",
|
||||
"<birthDate value=\"2016-04-14\"/>",
|
||||
|
|
|
@ -16,6 +16,7 @@ import ca.uhn.fhir.model.dstu2.resource.Patient;
|
|||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.parser.CustomResource364Dstu2.CustomResource364CustomDate;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.ElementUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
|
@ -55,19 +56,22 @@ public class CustomTypeDstu2Test {
|
|||
String string = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(mo);
|
||||
ourLog.info(string);
|
||||
|
||||
String medicationUuid = UuidUtils.findFirstUUID(string);
|
||||
assertNotNull(medicationUuid);
|
||||
|
||||
//@formatter:on
|
||||
assertThat(string).containsSubsequence(
|
||||
"<MedicationOrder xmlns=\"http://hl7.org/fhir\">",
|
||||
" <contained>",
|
||||
" <Medication xmlns=\"http://hl7.org/fhir\">",
|
||||
" <id value=\"1\"/>",
|
||||
" <id value=\"" + medicationUuid + "\"/>",
|
||||
" <code>",
|
||||
" <text value=\"MED TEXT\"/>",
|
||||
" </code>",
|
||||
" </Medication>",
|
||||
" </contained>",
|
||||
" <medication>",
|
||||
" <reference value=\"#1\"/>",
|
||||
" <reference value=\"#" + medicationUuid + "\"/>",
|
||||
" </medication>",
|
||||
"</MedicationOrder>");
|
||||
//@formatter:on
|
||||
|
@ -76,7 +80,7 @@ public class CustomTypeDstu2Test {
|
|||
|
||||
medication = (Medication) mo.getMedication().getResource();
|
||||
assertNotNull(medication);
|
||||
assertEquals("#1", medication.getId().getValue());
|
||||
assertEquals("#" + medicationUuid, medication.getId().getValue());
|
||||
assertEquals("MED TEXT", medication.getCode().getText());
|
||||
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
|||
import ca.uhn.fhir.parser.testprofile.CommunicationProfile;
|
||||
import ca.uhn.fhir.parser.testprofile.PatientProfile;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
|
@ -1705,14 +1706,16 @@ public class JsonParserDstu2Test {
|
|||
|
||||
String enc = parser.encodeResourceToString(o);
|
||||
ourLog.info(enc);
|
||||
String patientUuid = UuidUtils.findFirstUUID(enc);
|
||||
assertNotNull(patientUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(enc).containsSubsequence(
|
||||
"\"resourceType\": \"Observation\"",
|
||||
"\"contained\": [",
|
||||
"\"resourceType\": \"Patient\",",
|
||||
"\"id\": \"1\"",
|
||||
"\"reference\": \"#1\""
|
||||
"\"id\": \"" + patientUuid + "\"",
|
||||
"\"reference\": \"#" + patientUuid + "\""
|
||||
);
|
||||
//@formatter:on
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ import ca.uhn.fhir.model.primitive.StringDt;
|
|||
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -512,8 +513,10 @@ public class XmlParserDstu2Test {
|
|||
|
||||
String encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String organizationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuid);
|
||||
assertThat(encoded).contains("<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// Create a bundle with just the patient resource
|
||||
ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle();
|
||||
|
@ -522,35 +525,37 @@ public class XmlParserDstu2Test {
|
|||
// Encode the bundle
|
||||
encoded = xmlParser.encodeResourceToString(b);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<id value=\"1\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<id value=\"" + organizationUuid + "\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<entry>", "</entry>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<entry>.*</entry>.*<entry>");
|
||||
|
||||
// Re-parse the bundle
|
||||
patient = (Patient) xmlParser.parseResource(xmlParser.encodeResourceToString(patient));
|
||||
assertEquals("#1", patient.getManagingOrganization().getReference().getValue());
|
||||
assertEquals("#" + organizationUuid, patient.getManagingOrganization().getReference().getValue());
|
||||
|
||||
assertNotNull(patient.getManagingOrganization().getResource());
|
||||
org = (Organization) patient.getManagingOrganization().getResource();
|
||||
assertEquals("#1", org.getId().getValue());
|
||||
assertEquals("#" + organizationUuid, org.getId().getValue());
|
||||
assertEquals("Contained Test Organization", org.getName());
|
||||
|
||||
// And re-encode a second time
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>", "</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\""
|
||||
+ organizationUuid + "\"/>", "</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared
|
||||
patient.getContained().getContainedResources().clear();
|
||||
patient.getManagingOrganization().setReference((String) null);
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>", "</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"" + organizationUuid +
|
||||
"\"/>", "</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared and a manually set local ID
|
||||
patient.getContained().getContainedResources().clear();
|
||||
|
@ -581,6 +586,8 @@ public class XmlParserDstu2Test {
|
|||
|
||||
String output = parser.encodeResourceToString(dr);
|
||||
ourLog.info(output);
|
||||
String observationUuid = UuidUtils.findFirstUUID(output);
|
||||
assertNotNull(observationUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(output).containsSubsequence(
|
||||
|
@ -590,7 +597,7 @@ public class XmlParserDstu2Test {
|
|||
"</meta>",
|
||||
"<contained>",
|
||||
"<Observation xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + observationUuid + "\"/>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://custom_Observation\"/>",
|
||||
"</meta>",
|
||||
|
@ -599,7 +606,7 @@ public class XmlParserDstu2Test {
|
|||
"</contained>",
|
||||
"<status value=\"final\"/>",
|
||||
"<result>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + observationUuid + "\"/>",
|
||||
"</result>",
|
||||
"</DiagnosticReport>");
|
||||
//@formatter:on
|
||||
|
@ -611,7 +618,7 @@ public class XmlParserDstu2Test {
|
|||
dr = (CustomDiagnosticReportDstu2) parser.parseResource(output);
|
||||
assertEquals(DiagnosticReportStatusEnum.FINAL, dr.getStatusElement().getValueAsEnum());
|
||||
|
||||
assertEquals("#1", dr.getResult().get(0).getReference().getValueAsString());
|
||||
assertEquals("#" + observationUuid, dr.getResult().get(0).getReference().getValueAsString());
|
||||
obs = (CustomObservationDstu2) dr.getResult().get(0).getResource();
|
||||
assertEquals(ObservationStatusEnum.FINAL, obs.getStatusElement().getValueAsEnum());
|
||||
|
||||
|
@ -665,19 +672,21 @@ public class XmlParserDstu2Test {
|
|||
|
||||
String output = parser.encodeResourceToString(dr);
|
||||
ourLog.info(output);
|
||||
String observationUuid = UuidUtils.findFirstUUID(output);
|
||||
assertNotNull(observationUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(output).containsSubsequence(
|
||||
"<DiagnosticReport xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Observation xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + observationUuid + "\"/>",
|
||||
"<status value=\"final\"/>",
|
||||
"</Observation>",
|
||||
"</contained>",
|
||||
"<status value=\"final\"/>",
|
||||
"<result>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + observationUuid + "\"/>",
|
||||
"</result>",
|
||||
"</DiagnosticReport>");
|
||||
//@formatter:on
|
||||
|
@ -689,7 +698,7 @@ public class XmlParserDstu2Test {
|
|||
dr = (DiagnosticReport) parser.parseResource(output);
|
||||
assertEquals(DiagnosticReportStatusEnum.FINAL, dr.getStatusElement().getValueAsEnum());
|
||||
|
||||
assertEquals("#1", dr.getResult().get(0).getReference().getValueAsString());
|
||||
assertEquals("#" + observationUuid, dr.getResult().get(0).getReference().getValueAsString());
|
||||
obs = (Observation) dr.getResult().get(0).getResource();
|
||||
assertEquals(ObservationStatusEnum.FINAL, obs.getStatusElement().getValueAsEnum());
|
||||
|
||||
|
@ -1305,10 +1314,12 @@ public class XmlParserDstu2Test {
|
|||
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
String encoded = p.encodeResourceToString(medicationPrescript);
|
||||
ourLog.info(encoded);
|
||||
String medicationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(medicationUuid);
|
||||
|
||||
//@formatter:on
|
||||
assertThat(encoded).containsSubsequence("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"1\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#1\"/>",
|
||||
assertThat(encoded).containsSubsequence("<MedicationOrder xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"" + medicationUuid + "\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#" + medicationUuid + "\"/>",
|
||||
"<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationOrder>");
|
||||
//@formatter:off
|
||||
}
|
||||
|
@ -1561,13 +1572,15 @@ public class XmlParserDstu2Test {
|
|||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).containsSubsequence(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Condition xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + conditionUuid + "\"/>",
|
||||
"<bodySite>",
|
||||
"<text value=\"BODY SITE\"/>",
|
||||
"</bodySite>",
|
||||
|
@ -1575,7 +1588,7 @@ public class XmlParserDstu2Test {
|
|||
"</contained>",
|
||||
"<extension url=\"testCondition\">",
|
||||
"<valueReference>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + conditionUuid + "\"/>",
|
||||
"</valueReference>",
|
||||
"</extension>",
|
||||
"<birthDate value=\"2016-04-17\"/>",
|
||||
|
@ -2535,15 +2548,17 @@ public class XmlParserDstu2Test {
|
|||
|
||||
String enc = parser.encodeResourceToString(o);
|
||||
ourLog.info(enc);
|
||||
String patientUuid = UuidUtils.findFirstUUID(enc);
|
||||
assertNotNull(patientUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(enc).containsSubsequence(
|
||||
"<Observation xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + patientUuid + "\"/>",
|
||||
"</contained>",
|
||||
"<reference value=\"#1\"/>"
|
||||
"<reference value=\"#" + patientUuid + "\"/>"
|
||||
);
|
||||
//@formatter:on
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import ca.uhn.fhir.parser.PatientWithExtendedContactDstu3.CustomContactComponent
|
|||
import ca.uhn.fhir.parser.XmlParserDstu3Test.TestPatientFor327;
|
||||
import ca.uhn.fhir.parser.json.BaseJsonLikeValue.ScalarType;
|
||||
import ca.uhn.fhir.parser.json.BaseJsonLikeValue.ValueType;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
|
@ -648,6 +649,8 @@ public class JsonParserDstu3Test {
|
|||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).contains(
|
||||
|
@ -656,14 +659,14 @@ public class JsonParserDstu3Test {
|
|||
"\"contained\": [",
|
||||
"{",
|
||||
"\"resourceType\": \"Condition\",",
|
||||
"\"id\": \"1\"",
|
||||
"\"id\": \"" + conditionUuid + "\"",
|
||||
"}",
|
||||
"],",
|
||||
"\"extension\": [",
|
||||
"{",
|
||||
"\"url\": \"test\",",
|
||||
"\"valueReference\": {",
|
||||
"\"reference\": \"#1\"",
|
||||
"\"reference\": \"#" + conditionUuid + "\"",
|
||||
"}",
|
||||
"}",
|
||||
"],",
|
||||
|
@ -920,19 +923,21 @@ public class JsonParserDstu3Test {
|
|||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).contains(
|
||||
"\"resourceType\": \"Patient\"",
|
||||
"\"contained\": [",
|
||||
"\"resourceType\": \"Condition\"",
|
||||
"\"id\": \"1\"",
|
||||
"\"id\": \"" + conditionUuid + "\"",
|
||||
"\"bodySite\": [",
|
||||
"\"text\": \"BODY SITE\"",
|
||||
"\"extension\": [",
|
||||
"\"url\": \"testCondition\",",
|
||||
"\"valueReference\": {",
|
||||
"\"reference\": \"#1\"",
|
||||
"\"reference\": \"#" + conditionUuid + "\"",
|
||||
"\"birthDate\": \"2016-04-14\"",
|
||||
"}"
|
||||
);
|
||||
|
|
|
@ -13,6 +13,7 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
|||
import ca.uhn.fhir.parser.FooMessageHeaderWithExplicitField.FooMessageSourceComponent;
|
||||
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||
import ca.uhn.fhir.parser.PatientWithCustomCompositeExtension.FooParentExtension;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -560,8 +561,11 @@ public class XmlParserDstu3Test {
|
|||
|
||||
String encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String organizationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuid);
|
||||
|
||||
assertThat(encoded).contains("<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// Create a bundle with just the patient resource
|
||||
Bundle b = new Bundle();
|
||||
|
@ -570,35 +574,35 @@ public class XmlParserDstu3Test {
|
|||
// Encode the bundle
|
||||
encoded = xmlParser.encodeResourceToString(b);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).contains(Arrays.asList("<contained>", "<id value=\"1\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains(Arrays.asList("<contained>", "<id value=\"" + organizationUuid + "\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
assertThat(encoded).contains(Arrays.asList("<entry>", "</entry>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<entry>.*</entry>.*<entry>");
|
||||
|
||||
// Re-parse the bundle
|
||||
patient = (Patient) xmlParser.parseResource(xmlParser.encodeResourceToString(patient));
|
||||
assertEquals("#1", patient.getManagingOrganization().getReference());
|
||||
assertEquals("#" + organizationUuid, patient.getManagingOrganization().getReference());
|
||||
|
||||
assertNotNull(patient.getManagingOrganization().getResource());
|
||||
org = (Organization) patient.getManagingOrganization().getResource();
|
||||
assertEquals("#1", org.getIdElement().getValue());
|
||||
assertEquals("#" + organizationUuid, org.getIdElement().getValue());
|
||||
assertEquals("Contained Test Organization", org.getName());
|
||||
|
||||
// And re-encode a second time
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).contains(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>", "</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).contains(Arrays.asList("<contained>", "<Organization ", "<id value=\"" + organizationUuid + "\"/>", "</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared
|
||||
patient.getContained().clear();
|
||||
patient.getManagingOrganization().setReference(null);
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).contains(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>", "</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).contains(Arrays.asList("<contained>", "<Organization ", "<id value=\"" + organizationUuid + "\"/>", "</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared and a manually set local ID
|
||||
patient.getContained().clear();
|
||||
|
@ -629,6 +633,8 @@ public class XmlParserDstu3Test {
|
|||
|
||||
String output = parser.encodeResourceToString(dr);
|
||||
ourLog.info(output);
|
||||
String observationUuid = UuidUtils.findFirstUUID(output);
|
||||
assertNotNull(observationUuid);
|
||||
|
||||
assertThat(output).contains(
|
||||
"<DiagnosticReport xmlns=\"http://hl7.org/fhir\">",
|
||||
|
@ -637,7 +643,7 @@ public class XmlParserDstu3Test {
|
|||
"</meta>",
|
||||
"<contained>",
|
||||
"<Observation xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + observationUuid + "\"/>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://custom_Observation\"/>",
|
||||
"</meta>",
|
||||
|
@ -646,7 +652,7 @@ public class XmlParserDstu3Test {
|
|||
"</contained>",
|
||||
"<status value=\"final\"/>",
|
||||
"<result>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + observationUuid + "\"/>",
|
||||
"</result>",
|
||||
"</DiagnosticReport>");
|
||||
|
||||
|
@ -657,7 +663,7 @@ public class XmlParserDstu3Test {
|
|||
dr = (CustomDiagnosticReport) parser.parseResource(output);
|
||||
assertEquals(DiagnosticReportStatus.FINAL, dr.getStatus());
|
||||
|
||||
assertEquals("#1", dr.getResult().get(0).getReference());
|
||||
assertEquals("#" + observationUuid, dr.getResult().get(0).getReference());
|
||||
obs = (CustomObservation) dr.getResult().get(0).getResource();
|
||||
assertEquals(ObservationStatus.FINAL, obs.getStatus());
|
||||
|
||||
|
@ -680,18 +686,20 @@ public class XmlParserDstu3Test {
|
|||
|
||||
String output = parser.encodeResourceToString(dr);
|
||||
ourLog.info(output);
|
||||
String observationUuid = UuidUtils.findFirstUUID(output);
|
||||
assertNotNull(observationUuid);
|
||||
|
||||
assertThat(output).contains(
|
||||
"<DiagnosticReport xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Observation xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + observationUuid + "\"/>",
|
||||
"<status value=\"final\"/>",
|
||||
"</Observation>",
|
||||
"</contained>",
|
||||
"<status value=\"final\"/>",
|
||||
"<result>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + observationUuid + "\"/>",
|
||||
"</result>",
|
||||
"</DiagnosticReport>");
|
||||
|
||||
|
@ -702,7 +710,7 @@ public class XmlParserDstu3Test {
|
|||
dr = (DiagnosticReport) parser.parseResource(output);
|
||||
assertEquals(DiagnosticReportStatus.FINAL, dr.getStatus());
|
||||
|
||||
assertEquals("#1", dr.getResult().get(0).getReference());
|
||||
assertEquals("#" + observationUuid, dr.getResult().get(0).getReference());
|
||||
obs = (Observation) dr.getResult().get(0).getResource();
|
||||
assertEquals(ObservationStatus.FINAL, obs.getStatus());
|
||||
|
||||
|
@ -1282,17 +1290,19 @@ public class XmlParserDstu3Test {
|
|||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
assertThat(encoded).contains(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Condition xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + conditionUuid + "\"/>",
|
||||
"</Condition>",
|
||||
"</contained>",
|
||||
"<extension url=\"test\">",
|
||||
"<valueReference>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + conditionUuid + "\"/>",
|
||||
"</valueReference>",
|
||||
"</extension>",
|
||||
"<birthDate value=\"2016-04-05\"/>",
|
||||
|
@ -1359,10 +1369,12 @@ public class XmlParserDstu3Test {
|
|||
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||
String encoded = p.encodeResourceToString(medicationPrescript);
|
||||
ourLog.info(encoded);
|
||||
String medicationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(medicationUuid);
|
||||
|
||||
assertThat(encoded).contains
|
||||
("<MedicationRequest xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"1\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#1\"/>",
|
||||
("<MedicationRequest xmlns=\"http://hl7.org/fhir\">", "<contained>", "<Medication xmlns=\"http://hl7.org/fhir\">", "<id value=\"" + medicationUuid + "\"/>", "<code>", "<coding>",
|
||||
"<system value=\"urn:sys\"/>", "<code value=\"code1\"/>", "</coding>", "</code>", "</Medication>", "</contained>", "<medicationReference>", "<reference value=\"#" + medicationUuid + "\"/>",
|
||||
"<display value=\"MedRef\"/>", "</medicationReference>", "</MedicationRequest>");
|
||||
|
||||
}
|
||||
|
@ -1726,12 +1738,14 @@ public class XmlParserDstu3Test {
|
|||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
assertThat(encoded).contains(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Condition xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + conditionUuid + "\"/>",
|
||||
"<bodySite>",
|
||||
"<text value=\"BODY SITE\"/>",
|
||||
"</bodySite>",
|
||||
|
@ -1739,7 +1753,7 @@ public class XmlParserDstu3Test {
|
|||
"</contained>",
|
||||
"<extension url=\"testCondition\">",
|
||||
"<valueReference>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + conditionUuid + "\"/>",
|
||||
"</valueReference>",
|
||||
"</extension>",
|
||||
"<birthDate value=\"2016-04-14\"/>",
|
||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import net.sf.json.JSON;
|
||||
|
@ -58,6 +59,7 @@ import java.io.StringReader;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static ca.uhn.fhir.test.utilities.UuidUtils.UUID_PATTERN;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
@ -408,7 +410,10 @@ public class JsonParserHl7OrgDstu2Test {
|
|||
|
||||
String encoded = jsonParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\""));
|
||||
String organizationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuid);
|
||||
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"" + organizationUuid + "\"", "\"identifier\"", "\"reference\": \"#" + organizationUuid + "\""));
|
||||
|
||||
// Create a bundle with just the patient resource
|
||||
Bundle b = new Bundle();
|
||||
|
@ -417,21 +422,21 @@ public class JsonParserHl7OrgDstu2Test {
|
|||
// Encode the bundle
|
||||
encoded = jsonParser.encodeResourceToString(b);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\""));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"" + organizationUuid + "\"", "\"identifier\"", "\"reference\": \"#" + organizationUuid + "\""));
|
||||
|
||||
// Re-parse the bundle
|
||||
patient = (Patient) jsonParser.parseResource(jsonParser.encodeResourceToString(patient));
|
||||
assertEquals("#1", patient.getManagingOrganization().getReference());
|
||||
assertEquals("#" + organizationUuid, patient.getManagingOrganization().getReference());
|
||||
|
||||
assertNotNull(patient.getManagingOrganization().getResource());
|
||||
org = (Organization) patient.getManagingOrganization().getResource();
|
||||
assertEquals("#1", org.getIdElement().getValue());
|
||||
assertEquals("#" + organizationUuid, org.getIdElement().getValue());
|
||||
assertEquals("Contained Test Organization", org.getName());
|
||||
|
||||
// And re-encode a second time
|
||||
encoded = jsonParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\""));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"" + organizationUuid + "\"", "\"identifier\"", "\"reference\": \"#" + organizationUuid + "\""));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)\"contained\":.*\\[.*\"contained\":");
|
||||
|
||||
// And re-encode once more, with the references cleared
|
||||
|
@ -439,7 +444,7 @@ public class JsonParserHl7OrgDstu2Test {
|
|||
patient.getManagingOrganization().setReference(null);
|
||||
encoded = jsonParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"1\"", "\"identifier\"", "\"reference\": \"#1\""));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\": [", "\"id\": \"" + organizationUuid + "\"", "\"identifier\"", "\"reference\": \"#" + organizationUuid + "\""));
|
||||
assertThat(encoded).doesNotContainPattern("(?s).*\"contained\":.*\\[.*\"contained\":");
|
||||
|
||||
// And re-encode once more, with the references cleared and a manually set local ID
|
||||
|
@ -472,13 +477,16 @@ public class JsonParserHl7OrgDstu2Test {
|
|||
// Encode the buntdle
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(b);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\"", "resourceType\": \"Organization", "id\": \"1\""));
|
||||
assertThat(encoded).contains("reference\": \"#1\"");
|
||||
String organizationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuid);
|
||||
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\"", "resourceType\": \"Organization", "id\": \"" + organizationUuid + "\""));
|
||||
assertThat(encoded).contains("reference\": \"#" + organizationUuid + "\"");
|
||||
|
||||
encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\"", "resourceType\": \"Organization", "id\": \"1\""));
|
||||
assertThat(encoded).contains("reference\": \"#1\"");
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("\"contained\"", "resourceType\": \"Organization", "id\": \"" + organizationUuid + "\""));
|
||||
assertThat(encoded).contains("reference\": \"#" + organizationUuid + "\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -696,7 +704,7 @@ public class JsonParserHl7OrgDstu2Test {
|
|||
String enc = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(list);
|
||||
ourLog.info(enc);
|
||||
|
||||
assertThat(enc).contains("\"id\": \"1\"");
|
||||
assertThat(enc).containsPattern("\"id\": \"" + UUID_PATTERN);
|
||||
|
||||
List_ parsed = ourCtx.newJsonParser().parseResource(List_.class,enc);
|
||||
assertEquals(Patient.class, parsed.getEntry().get(0).getItem().getResource().getClass());
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
|||
import ca.uhn.fhir.parser.JsonParserHl7OrgDstu2Test.MyPatientWithOneDeclaredAddressExtension;
|
||||
import ca.uhn.fhir.parser.JsonParserHl7OrgDstu2Test.MyPatientWithOneDeclaredExtension;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.test.utilities.UuidUtils;
|
||||
import net.sf.json.JSON;
|
||||
import net.sf.json.JSONSerializer;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -216,8 +217,11 @@ public class XmlParserHl7OrgDstu2Test {
|
|||
|
||||
String encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String organizationUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(organizationUuid);
|
||||
|
||||
assertThat(encoded).contains("<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// Create a bundle with just the patient resource
|
||||
Bundle b = new Bundle();
|
||||
|
@ -226,37 +230,37 @@ public class XmlParserHl7OrgDstu2Test {
|
|||
// Encode the bundle
|
||||
encoded = xmlParser.encodeResourceToString(b);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<id value=\"1\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<id value=\"" + organizationUuid + "\"/>", "</contained>"));
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<entry>", "</entry>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<entry>.*</entry>.*<entry>");
|
||||
|
||||
// Re-parse the bundle
|
||||
patient = (Patient) xmlParser.parseResource(xmlParser.encodeResourceToString(patient));
|
||||
assertEquals("#1", patient.getManagingOrganization().getReferenceElement().getValue());
|
||||
assertEquals("#" + organizationUuid, patient.getManagingOrganization().getReferenceElement().getValue());
|
||||
|
||||
assertNotNull(patient.getManagingOrganization().getResource());
|
||||
org = (Organization) patient.getManagingOrganization().getResource();
|
||||
assertEquals("#1", org.getIdElement().getValue());
|
||||
assertEquals("#" + organizationUuid, org.getIdElement().getValue());
|
||||
assertEquals("Contained Test Organization", org.getName());
|
||||
|
||||
// And re-encode a second time
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>",
|
||||
"</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"" + organizationUuid + "\"/>",
|
||||
"</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared
|
||||
patient.getContained().clear();
|
||||
patient.getManagingOrganization().setReference(null);
|
||||
encoded = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"1\"/>",
|
||||
"</Organization", "</contained>", "<reference value=\"#1\"/>"));
|
||||
assertThat(encoded).containsSubsequence(Arrays.asList("<contained>", "<Organization ", "<id value=\"" + organizationUuid + "\"/>",
|
||||
"</Organization", "</contained>", "<reference value=\"#" + organizationUuid + "\"/>"));
|
||||
assertThat(encoded).doesNotContainPattern("(?s)<contained>.*<Org.*<contained>");
|
||||
assertThat(encoded).contains("<reference value=\"#1\"/>");
|
||||
assertThat(encoded).contains("<reference value=\"#" + organizationUuid + "\"/>");
|
||||
|
||||
// And re-encode once more, with the references cleared and a manually set
|
||||
// local ID
|
||||
|
@ -969,13 +973,15 @@ public class XmlParserHl7OrgDstu2Test {
|
|||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
|
||||
ourLog.info(encoded);
|
||||
String conditionUuid = UuidUtils.findFirstUUID(encoded);
|
||||
assertNotNull(conditionUuid);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded).containsSubsequence(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<contained>",
|
||||
"<Condition xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"1\"/>",
|
||||
"<id value=\"" + conditionUuid + "\"/>",
|
||||
"<bodySite>",
|
||||
"<text value=\"BODY SITE\"/>",
|
||||
"</bodySite>",
|
||||
|
@ -983,7 +989,7 @@ public class XmlParserHl7OrgDstu2Test {
|
|||
"</contained>",
|
||||
"<extension url=\"testCondition\">",
|
||||
"<valueReference>",
|
||||
"<reference value=\"#1\"/>",
|
||||
"<reference value=\"#" + conditionUuid + "\"/>",
|
||||
"</valueReference>",
|
||||
"</extension>",
|
||||
"<birthDate value=\"2016-04-14\"/>",
|
||||
|
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.i18n.Msg;
|
|||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.test.BaseTest;
|
||||
import ca.uhn.fhir.util.BundleBuilder;
|
||||
import ca.uhn.fhir.util.StopWatch;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -23,10 +24,12 @@ import org.hl7.fhir.r4.model.Composition;
|
|||
import org.hl7.fhir.r4.model.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.Device;
|
||||
import org.hl7.fhir.r4.model.DiagnosticReport;
|
||||
import org.hl7.fhir.r4.model.DocumentReference;
|
||||
import org.hl7.fhir.r4.model.Encounter;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.HumanName;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Identifier;
|
||||
import org.hl7.fhir.r4.model.Medication;
|
||||
import org.hl7.fhir.r4.model.MedicationDispense;
|
||||
|
@ -43,6 +46,7 @@ import org.hl7.fhir.r4.model.PrimitiveType;
|
|||
import org.hl7.fhir.r4.model.Quantity;
|
||||
import org.hl7.fhir.r4.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.Specimen;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.Type;
|
||||
import org.hl7.fhir.r4.model.codesystems.DataAbsentReason;
|
||||
|
@ -55,7 +59,10 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import org.testcontainers.shaded.com.trilead.ssh2.packets.PacketDisconnect;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Ref;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
|
@ -261,6 +268,36 @@ public class JsonParserR4Test extends BaseTest {
|
|||
|
||||
idx = encoded.indexOf("\"Medication\"", idx + 1);
|
||||
assertEquals(-1, idx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateContainedResourcesAcrossABundleAreReplicated() {
|
||||
Bundle b = new Bundle();
|
||||
Specimen specimen = new Specimen();
|
||||
Practitioner practitioner = new Practitioner();
|
||||
DiagnosticReport report = new DiagnosticReport();
|
||||
report.addSpecimen(new Reference(specimen));
|
||||
b.addEntry().setResource(report).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("/DiagnosticReport");
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.addPerformer(new Reference(practitioner));
|
||||
obs.setSpecimen(new Reference(specimen));
|
||||
|
||||
b.addEntry().setResource(obs).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("/Observation");
|
||||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(b);
|
||||
//Then: Diag should contain one local contained specimen
|
||||
assertThat(encoded).contains("[{\"resource\":{\"resourceType\":\"DiagnosticReport\",\"contained\":[{\"resourceType\":\"Specimen\",\"id\":\""+ specimen.getId().replaceFirst("#", "") +"\"}]");
|
||||
//Then: Obs should contain one local contained specimen, and one local contained pract
|
||||
assertThat(encoded).contains("\"resource\":{\"resourceType\":\"Observation\",\"contained\":[{\"resourceType\":\"Specimen\",\"id\":\""+ specimen.getId().replaceFirst("#", "") +"\"},{\"resourceType\":\"Practitioner\",\"id\":\"" + practitioner.getId().replaceAll("#","") + "\"}]");
|
||||
assertThat(encoded).contains("\"performer\":[{\"reference\":\""+practitioner.getId()+"\"}],\"specimen\":{\"reference\":\""+specimen.getId()+"\"}");
|
||||
|
||||
//Also, reverting the operation should work too!
|
||||
Bundle bundle = ourCtx.newJsonParser().parseResource(Bundle.class, encoded);
|
||||
IBaseResource resource1 = ((DiagnosticReport) bundle.getEntry().get(0).getResource()).getSpecimenFirstRep().getResource();
|
||||
IBaseResource resource = ((Observation) bundle.getEntry().get(1).getResource()).getSpecimen().getResource();
|
||||
assertThat(resource1.getIdElement().getIdPart()).isEqualTo(resource.getIdElement().getIdPart());
|
||||
assertThat(resource1).isNotSameAs(resource);
|
||||
|
||||
}
|
||||
|
||||
|
@ -279,8 +316,9 @@ public class JsonParserR4Test extends BaseTest {
|
|||
|
||||
ourCtx.getParserOptions().setAutoContainReferenceTargetsWithNoId(true);
|
||||
encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(md);
|
||||
assertEquals("{\"resourceType\":\"MedicationDispense\",\"contained\":[{\"resourceType\":\"Medication\",\"id\":\"1\",\"code\":{\"text\":\"MED\"}}],\"identifier\":[{\"value\":\"DISPENSE\"}],\"medicationReference\":{\"reference\":\"#1\"}}", encoded);
|
||||
|
||||
String guidWithHash = med.getId();
|
||||
String withoutHash = guidWithHash.replace("#", "");
|
||||
assertThat(encoded).contains("{\"resourceType\":\"MedicationDispense\",\"contained\":[{\"resourceType\":\"Medication\",\"id\":\"" + withoutHash + "\",\"code\":{\"text\":\"MED\"}}],\"identifier\":[{\"value\":\"DISPENSE\"}],\"medicationReference\":{\"reference\":\"" + guidWithHash +"\"}}"); //Note we dont check exact ID since its a GUID
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -571,7 +609,7 @@ public class JsonParserR4Test extends BaseTest {
|
|||
|
||||
obs = ourCtx.newJsonParser().parseResource(Observation.class, encoded);
|
||||
assertEquals("#1", obs.getContained().get(0).getId());
|
||||
assertEquals("#2", obs.getContained().get(1).getId());
|
||||
assertEquals(enc.getId(), obs.getContained().get(1).getId());
|
||||
|
||||
pt = (Patient) obs.getSubject().getResource();
|
||||
assertEquals("FAM", pt.getNameFirstRep().getFamily());
|
||||
|
@ -600,7 +638,7 @@ public class JsonParserR4Test extends BaseTest {
|
|||
|
||||
obs = ourCtx.newJsonParser().parseResource(Observation.class, encoded);
|
||||
assertEquals("#1", obs.getContained().get(0).getId());
|
||||
assertEquals("#2", obs.getContained().get(1).getId());
|
||||
assertEquals(pt.getId(), obs.getContained().get(1).getId());
|
||||
|
||||
pt = (Patient) obs.getSubject().getResource();
|
||||
assertEquals("FAM", pt.getNameFirstRep().getFamily());
|
||||
|
@ -620,13 +658,14 @@ public class JsonParserR4Test extends BaseTest {
|
|||
ourLog.info(encoded);
|
||||
mr = ourCtx.newJsonParser().parseResource(MedicationRequest.class, encoded);
|
||||
|
||||
mr.setMedication(new Reference(new Medication().setStatus(Medication.MedicationStatus.ACTIVE)));
|
||||
Medication med = new Medication().setStatus(Medication.MedicationStatus.ACTIVE);
|
||||
mr.setMedication(new Reference(med));
|
||||
encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(mr);
|
||||
ourLog.info(encoded);
|
||||
mr = ourCtx.newJsonParser().parseResource(MedicationRequest.class, encoded);
|
||||
|
||||
assertEquals("#1", mr.getContained().get(0).getId());
|
||||
assertEquals("#2", mr.getContained().get(1).getId());
|
||||
assertEquals(pract.getId(), mr.getContained().get(0).getId());
|
||||
assertEquals(med.getId(), mr.getContained().get(1).getId());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,10 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.model.api.annotation.Block;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.JsonParser;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.jena.base.Sys;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
|
@ -47,6 +50,8 @@ import org.junit.jupiter.api.Test;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
@ -62,6 +67,7 @@ import java.util.concurrent.Future;
|
|||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static ca.uhn.fhir.test.utilities.UuidUtils.HASH_UUID_PATTERN;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
@ -188,12 +194,12 @@ public class FhirTerserR4Test {
|
|||
|
||||
FhirTerser.ContainedResources contained = myCtx.newTerser().containResources(mr, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
|
||||
|
||||
assertEquals("#1", mr.getContained().get(0).getId());
|
||||
assertEquals("#2", mr.getContained().get(1).getId());
|
||||
assertThat(mr.getContained().get(0).getId()).containsPattern(HASH_UUID_PATTERN);
|
||||
assertThat(mr.getContained().get(1).getId()).containsPattern(HASH_UUID_PATTERN);
|
||||
assertEquals(ResourceType.Medication, mr.getContained().get(0).getResourceType());
|
||||
assertEquals(ResourceType.Practitioner, mr.getContained().get(1).getResourceType());
|
||||
assertEquals("#1", mr.getMedicationReference().getReference());
|
||||
assertEquals("#2", mr.getRequester().getReference());
|
||||
assertEquals(mr.getContained().get(0).getId(), mr.getMedicationReference().getReference());
|
||||
assertEquals(mr.getContained().get(1).getId(), mr.getRequester().getReference());
|
||||
|
||||
FhirTerser.ContainedResources secondPass = myCtx.newTerser().containResources(mr, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
|
||||
assertThat(secondPass).isSameAs(contained);
|
||||
|
@ -212,12 +218,12 @@ public class FhirTerserR4Test {
|
|||
|
||||
myCtx.newTerser().containResources(medAdmin, FhirTerser.OptionsEnum.MODIFY_RESOURCE, FhirTerser.OptionsEnum.STORE_AND_REUSE_RESULTS);
|
||||
|
||||
assertEquals("#1", medAdmin.getContained().get(0).getId());
|
||||
assertEquals("#2", medAdmin.getContained().get(1).getId());
|
||||
assertThat(medAdmin.getContained().get(0).getId()).containsPattern(HASH_UUID_PATTERN);
|
||||
assertThat(medAdmin.getContained().get(1).getId()).containsPattern(HASH_UUID_PATTERN);
|
||||
assertEquals(ResourceType.Medication, medAdmin.getContained().get(0).getResourceType());
|
||||
assertEquals(ResourceType.Substance, medAdmin.getContained().get(1).getResourceType());
|
||||
assertEquals("#1", medAdmin.getMedicationReference().getReference());
|
||||
assertEquals("#2", ((Medication) (medAdmin.getContained().get(0))).getIngredientFirstRep().getItemReference().getReference());
|
||||
assertEquals(medAdmin.getContained().get(0).getId(), medAdmin.getMedicationReference().getReference());
|
||||
assertEquals(medAdmin.getContained().get(1).getId(), ((Medication) (medAdmin.getContained().get(0))).getIngredientFirstRep().getItemReference().getReference());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1545,23 +1551,29 @@ public class FhirTerserR4Test {
|
|||
|
||||
@Test
|
||||
void copyingAndParsingCreatesDuplicateContainedResources() {
|
||||
var input = new Library();
|
||||
var library = new Library();
|
||||
var params = new Parameters();
|
||||
var id = "#expansion-parameters-ecr";
|
||||
params.setId(id);
|
||||
params.addParameter("system-version", new StringType("test2"));
|
||||
var paramsExt = new Extension();
|
||||
|
||||
paramsExt.setUrl("test").setValue(new Reference(id));
|
||||
input.addContained(params);
|
||||
input.addExtension(paramsExt);
|
||||
library.addContained(params);
|
||||
library.addExtension(paramsExt);
|
||||
|
||||
final var parser = FhirContext.forR4Cached().newJsonParser();
|
||||
var stringified = parser.encodeResourceToString(input);
|
||||
var stringified = parser.encodeResourceToString(library);
|
||||
|
||||
|
||||
var parsed = parser.parseResource(stringified);
|
||||
var copy = ((Library) parsed).copy();
|
||||
|
||||
assertEquals(1, copy.getContained().size());
|
||||
var stringifiedCopy = parser.encodeResourceToString(copy);
|
||||
var parsedCopy = parser.parseResource(stringifiedCopy);
|
||||
assertEquals(1, ((Library) parsedCopy).getContained().size());
|
||||
|
||||
String stringifiedCopy = FhirContext.forR4Cached().newJsonParser().encodeResourceToString(copy);
|
||||
Library parsedCopy = (Library) parser.parseResource(stringifiedCopy);
|
||||
assertEquals(1, parsedCopy.getContained().size());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Test Utilities
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.test.utilities;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UuidUtils {
|
||||
|
||||
private UuidUtils() {
|
||||
}
|
||||
|
||||
public static final String UUID_PATTERN = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
|
||||
public static final String HASH_UUID_PATTERN = "#" + UUID_PATTERN;
|
||||
private static final Pattern COMPILED_UUID_PATTERN = Pattern.compile(UUID_PATTERN);
|
||||
|
||||
/**
|
||||
* Extracts first UUID from String.
|
||||
* Returns null if no UUID present in the String.
|
||||
*/
|
||||
public static String findFirstUUID(String input) {
|
||||
Matcher matcher = COMPILED_UUID_PATTERN.matcher(input);
|
||||
|
||||
if (matcher.find()) {
|
||||
return matcher.group();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Test Utilities
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.test.utilities.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IDomainResource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface IValidationProviders {
|
||||
String CODE_SYSTEM = "http://code.system/url";
|
||||
String CODE_SYSTEM_VERSION = "1.0.0";
|
||||
String CODE_SYSTEM_NAME = "Test Code System";
|
||||
String CODE = "CODE";
|
||||
String VALUE_SET_URL = "http://value.set/url";
|
||||
String DISPLAY = "Explanation for code TestCode.";
|
||||
String LANGUAGE = "en";
|
||||
String ERROR_MESSAGE = "This is an error message";
|
||||
|
||||
interface IMyValidationProvider extends IResourceProvider {
|
||||
void addException(String theOperation, String theUrl, String theCode, Exception theException);
|
||||
<P extends IBaseParameters> void addTerminologyResponse(String theOperation, String theUrl, String theCode, P theReturnParams);
|
||||
IBaseParameters addTerminologyResponse(String theOperation, String theUrl, String theCode, FhirContext theFhirContext, String theTerminologyResponseFile);
|
||||
}
|
||||
|
||||
abstract class MyValidationProvider<T extends IDomainResource> implements IMyValidationProvider {
|
||||
private final Map<String, Exception> myExceptionMap = new HashMap<>();
|
||||
private boolean myShouldThrowExceptionForResourceNotFound = true;
|
||||
private final Map<String, IBaseParameters> myTerminologyResponseMap = new HashMap<>();
|
||||
private final Map<String, T> myTerminologyResourceMap = new HashMap<>();
|
||||
|
||||
static String getInputKey(String theOperation, String theUrl, String theCode) {
|
||||
return theOperation + "-" + theUrl + "#" + theCode;
|
||||
}
|
||||
|
||||
public void setShouldThrowExceptionForResourceNotFound(boolean theShouldThrowExceptionForResourceNotFound) {
|
||||
myShouldThrowExceptionForResourceNotFound = theShouldThrowExceptionForResourceNotFound;
|
||||
}
|
||||
|
||||
public void addException(String theOperation, String theUrl, String theCode, Exception theException) {
|
||||
String inputKey = getInputKey(theOperation, theUrl, theCode);
|
||||
myExceptionMap.put(inputKey, theException);
|
||||
}
|
||||
|
||||
abstract Class<? extends IBaseParameters> getParameterType();
|
||||
|
||||
@Override
|
||||
public <P extends IBaseParameters> void addTerminologyResponse(String theOperation, String theUrl, String theCode, P theReturnParams) {
|
||||
myTerminologyResponseMap.put(getInputKey(theOperation, theUrl, theCode), theReturnParams);
|
||||
}
|
||||
|
||||
public IBaseParameters addTerminologyResponse(String theOperation, String theUrl, String theCode, FhirContext theFhirContext, String theTerminologyResponseFile) {
|
||||
IBaseParameters responseParams = ClasspathUtil.loadResource(theFhirContext, getParameterType(), theTerminologyResponseFile);
|
||||
addTerminologyResponse(theOperation, theUrl, theCode, responseParams);
|
||||
return responseParams;
|
||||
}
|
||||
|
||||
protected void addTerminologyResource(String theUrl, T theResource) {
|
||||
myTerminologyResourceMap.put(theUrl, theResource);
|
||||
}
|
||||
|
||||
public abstract T addTerminologyResource(String theUrl);
|
||||
|
||||
protected IBaseParameters getTerminologyResponse(String theOperation, String theUrl, String theCode) throws Exception {
|
||||
String inputKey = getInputKey(theOperation, theUrl, theCode);
|
||||
if (myExceptionMap.containsKey(inputKey)) {
|
||||
throw myExceptionMap.get(inputKey);
|
||||
}
|
||||
IBaseParameters params = myTerminologyResponseMap.get(inputKey);
|
||||
if (params == null) {
|
||||
throw new IllegalStateException("Test setup incomplete. Missing return params for " + inputKey);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
protected T getTerminologyResource(UriParam theUrlParam) {
|
||||
if (theUrlParam.isEmpty()) {
|
||||
throw new IllegalStateException("CodeSystem url should not be null.");
|
||||
}
|
||||
String urlValue = theUrlParam.getValue();
|
||||
if (!myTerminologyResourceMap.containsKey(urlValue) && myShouldThrowExceptionForResourceNotFound) {
|
||||
throw new IllegalStateException("Test setup incomplete. CodeSystem not found " + urlValue);
|
||||
}
|
||||
return myTerminologyResourceMap.get(urlValue);
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<T> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
T resource = getTerminologyResource(theUrlParam);
|
||||
return resource != null ? List.of(resource) : List.of();
|
||||
}
|
||||
}
|
||||
|
||||
interface IMyLookupCodeProvider extends IResourceProvider {
|
||||
void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Test Utilities
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.test.utilities.validation;
|
||||
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IValidationProvidersDstu3 {
|
||||
@SuppressWarnings("unused")
|
||||
class MyCodeSystemProviderDstu3 extends IValidationProviders.MyValidationProvider<CodeSystem> {
|
||||
@Operation(name = "$validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public IBaseParameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
||||
) throws Exception {
|
||||
String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$validate-code", url, code);
|
||||
}
|
||||
|
||||
@Operation(name = "$lookup", idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public IBaseParameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system",max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) throws Exception {
|
||||
String url = theSystem != null ? theSystem.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$lookup", url, code);
|
||||
}
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
@Override
|
||||
Class<Parameters> getParameterType() {
|
||||
return Parameters.class;
|
||||
}
|
||||
@Override
|
||||
public CodeSystem addTerminologyResource(String theUrl) {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
|
||||
codeSystem.setUrl(theUrl);
|
||||
addTerminologyResource(theUrl, codeSystem);
|
||||
return codeSystem;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
class MyValueSetProviderDstu3 extends IValidationProviders.MyValidationProvider<ValueSet> {
|
||||
@Operation(name = "$validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public IBaseParameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||
) throws Exception {
|
||||
String url = theValueSetUrl != null ? theValueSetUrl.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$validate-code", url, code);
|
||||
}
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return ValueSet.class;
|
||||
}
|
||||
@Override
|
||||
Class<Parameters> getParameterType() {
|
||||
return Parameters.class;
|
||||
}
|
||||
@Override
|
||||
public ValueSet addTerminologyResource(String theUrl) {
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
|
||||
valueSet.setUrl(theUrl);
|
||||
addTerminologyResource(theUrl, valueSet);
|
||||
return valueSet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,29 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR Test Utilities
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
package ca.uhn.fhir.test.utilities.validation;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
|
@ -21,38 +38,29 @@ import org.hl7.fhir.r4.model.ValueSet;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public interface IValidateCodeProvidersR4 {
|
||||
public interface IValidationProvidersR4 {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
class MyCodeSystemProviderR4 implements IValidationProviders.IMyCodeSystemProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private StringType myDisplay;
|
||||
private Exception myException;
|
||||
private Parameters myReturnParams;
|
||||
class MyCodeSystemProviderR4 extends IValidationProviders.MyValidationProvider<CodeSystem> {
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@Operation(name = "$validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
public IBaseParameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
||||
) throws Exception {
|
||||
mySystemUrl = theCodeSystemUrl;
|
||||
myCode = theCode;
|
||||
myDisplay = theDisplay;
|
||||
if (myException != null) {
|
||||
throw myException;
|
||||
}
|
||||
return myReturnParams;
|
||||
String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$validate-code", url, code);
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@Operation(name = "$lookup", idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
|
@ -69,54 +77,39 @@ public interface IValidateCodeProvidersR4 {
|
|||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) throws Exception {
|
||||
mySystemUrl = theSystem;
|
||||
myCode = theCode;
|
||||
if (myException != null) {
|
||||
throw myException;
|
||||
String url = theSystem != null ? theSystem.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$lookup", url, code);
|
||||
}
|
||||
return myReturnParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
public void setException(Exception theException) {
|
||||
myException = theException;
|
||||
}
|
||||
@Override
|
||||
public void setReturnParams(IBaseParameters theParameters) {
|
||||
myReturnParams = (Parameters) theParameters;
|
||||
Class<Parameters> getParameterType() {
|
||||
return Parameters.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
public String getDisplay() {
|
||||
return myDisplay != null ? myDisplay.getValue() : null;
|
||||
public CodeSystem addTerminologyResource(String theUrl) {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
|
||||
codeSystem.setUrl(theUrl);
|
||||
addTerminologyResource(theUrl, codeSystem);
|
||||
return codeSystem;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
class MyValueSetProviderR4 implements IValidationProviders.IMyValueSetProvider {
|
||||
private Exception myException;
|
||||
private Parameters myReturnParams;
|
||||
private UriType mySystemUrl;
|
||||
private UriType myValueSetUrl;
|
||||
private CodeType myCode;
|
||||
private StringType myDisplay;
|
||||
class MyValueSetProviderR4 extends IValidationProviders.MyValidationProvider<ValueSet> {
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@Operation(name = "$validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
public IBaseParameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
|
@ -125,41 +118,25 @@ public interface IValidateCodeProvidersR4 {
|
|||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||
) throws Exception {
|
||||
mySystemUrl = theSystem;
|
||||
myValueSetUrl = theValueSetUrl;
|
||||
myCode = theCode;
|
||||
myDisplay = theDisplay;
|
||||
if (myException != null) {
|
||||
throw myException;
|
||||
String url = theValueSetUrl != null ? theValueSetUrl.getValue() : null;
|
||||
String code = theCode != null ? theCode.getValue() : null;
|
||||
return getTerminologyResponse("$validate-code", url, code);
|
||||
}
|
||||
return myReturnParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return ValueSet.class;
|
||||
}
|
||||
public void setException(Exception theException) {
|
||||
myException = theException;
|
||||
@Override
|
||||
Class<Parameters> getParameterType() {
|
||||
return Parameters.class;
|
||||
}
|
||||
@Override
|
||||
public void setReturnParams(IBaseParameters theParameters) {
|
||||
myReturnParams = (Parameters) theParameters;
|
||||
}
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
@Override
|
||||
public String getValueSet() {
|
||||
return myValueSetUrl != null ? myValueSetUrl.getValueAsString() : null;
|
||||
}
|
||||
public String getDisplay() {
|
||||
return myDisplay != null ? myDisplay.getValue() : null;
|
||||
public ValueSet addTerminologyResource(String theUrl) {
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setId(theUrl.substring(0, theUrl.lastIndexOf("/")));
|
||||
valueSet.setUrl(theUrl);
|
||||
addTerminologyResource(theUrl, valueSet);
|
||||
return valueSet;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.test.utilities;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class UuidUtilsTest {
|
||||
|
||||
@Test
|
||||
void testFindsUuid() {
|
||||
String xml = "<id value=#cdb6dfa1-74b7-4ea9-88e0-d3afaef8c016/>";
|
||||
String uuid = UuidUtils.findFirstUUID(xml);
|
||||
assertEquals("cdb6dfa1-74b7-4ea9-88e0-d3afaef8c016", uuid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFindsFirstUuid() {
|
||||
String xml = "<id value=#cdb6dfa1-74b7-4ea9-88e0-d3afaef8c016/><id value=#da8a08e3-ddf5-4a62-baae-3e8c3ea04687/>";
|
||||
String uuid = UuidUtils.findFirstUUID(xml);
|
||||
assertEquals("cdb6dfa1-74b7-4ea9-88e0-d3afaef8c016", uuid);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoUuidReturnsNull() {
|
||||
String xml = "<id value=x />";
|
||||
assertNull(UuidUtils.findFirstUUID(xml));
|
||||
}
|
||||
}
|
|
@ -190,7 +190,7 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
|||
return new CodeValidationResult()
|
||||
.setSeverity(IssueSeverity.ERROR)
|
||||
.setMessage(theMessage)
|
||||
.setCodeValidationIssues(Collections.singletonList(new CodeValidationIssue(
|
||||
.setIssues(Collections.singletonList(new CodeValidationIssue(
|
||||
theMessage,
|
||||
IssueSeverity.ERROR,
|
||||
CodeValidationIssueCode.INVALID,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue