From f58fba6f04dc1c670d51b3abab009c157fc04b6a Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 2 Dec 2019 11:00:18 -0500 Subject: [PATCH] Some test cleanup (#1616) * Some test cleanup * Add test * More test logging * More docs cleanup * Add test logging --- .../uhn/fhir/rest/param/QualifierDetails.java | 14 - .../fhir/rest/client/method/MethodUtil.java | 6 +- .../rest/client/method/SearchParameter.java | 45 +-- .../uhn/hapi/fhir/docs/FhirTesterConfig.java | 20 + .../hapi/fhir/docs/introduction/versions.md | 286 +++++++------- .../java/ca/uhn/fhir/jpa/entity/Search.java | 4 + .../jpa/search/SearchCoordinatorSvcImpl.java | 2 +- .../fhir/jpa/term/BaseTermReadSvcImpl.java | 4 +- .../ResourceReindexingSvcImplTest.java | 37 ++ .../jpa/term/ValueSetExpansionR4Test.java | 74 +++- .../method/SearchMethodBindingTest.java | 174 +++++---- .../uhn/fhir/parser/XmlParserDstu2_1Test.java | 2 +- .../src/test/resources/bundle-circ-ref.json | 369 ++++++++++++++++++ 13 files changed, 753 insertions(+), 284 deletions(-) create mode 100644 hapi-fhir-structures-r4/src/test/resources/bundle-circ-ref.json diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/QualifierDetails.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/QualifierDetails.java index 2bfcb5e698f..bb76f60ba36 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/QualifierDetails.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/QualifierDetails.java @@ -42,20 +42,6 @@ public class QualifierDetails { } } } - /* - * This was removed Sep 9 2015, as I don't see any way it could possibly be triggered. - if (!theQualifierWhitelist.contains(SearchParameter.QUALIFIER_ANY_TYPE)) { - if (myColonQualifier != null) { - if (!theQualifierWhitelist.contains(myColonQualifier)) { - return false; - } - } else { - if (!theQualifierWhitelist.contains(":")) { - return false; - } - } - } - */ } if (theQualifierBlacklist != null) { if (myDotQualifier != null) { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java index 538d6264d67..1a3bfec5fc9 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/MethodUtil.java @@ -255,8 +255,7 @@ public class MethodUtil { parameter.setRequired(true); parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes()); parameter.setCompositeTypes(((RequiredParam) nextAnnotation).compositeTypes()); - parameter.setChainlists(((RequiredParam) nextAnnotation).chainWhitelist(), - ((RequiredParam) nextAnnotation).chainBlacklist()); + parameter.setChainlists(((RequiredParam) nextAnnotation).chainWhitelist()); parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType); param = parameter; } else if (nextAnnotation instanceof OptionalParam) { @@ -265,8 +264,7 @@ public class MethodUtil { parameter.setRequired(false); parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes()); parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes()); - parameter.setChainlists(((OptionalParam) nextAnnotation).chainWhitelist(), - ((OptionalParam) nextAnnotation).chainBlacklist()); + parameter.setChainlists(((OptionalParam) nextAnnotation).chainWhitelist()); parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType); param = parameter; } else if (nextAnnotation instanceof RawParam) { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java index ea64ed18965..9ba3c06df02 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java @@ -47,8 +47,8 @@ public class SearchParameter extends BaseQueryParameter { static final String QUALIFIER_ANY_TYPE = ":*"; static { - ourParamTypes = new HashMap, RestSearchParameterTypeEnum>(); - ourParamQualifiers = new HashMap>(); + ourParamTypes = new HashMap<>(); + ourParamQualifiers = new HashMap<>(); ourParamTypes.put(StringParam.class, RestSearchParameterTypeEnum.STRING); ourParamTypes.put(StringOrListParam.class, RestSearchParameterTypeEnum.STRING); @@ -100,11 +100,9 @@ public class SearchParameter extends BaseQueryParameter { private List> myCompositeTypes = Collections.emptyList(); private List> myDeclaredTypes; - private String myDescription; private String myName; private IParamBinder myParamBinder; private RestSearchParameterTypeEnum myParamType; - private Set myQualifierBlacklist; private Set myQualifierWhitelist; private boolean myRequired; private Class myType; @@ -124,7 +122,7 @@ public class SearchParameter extends BaseQueryParameter { */ @Override public List encode(FhirContext theContext, Object theObject) throws InternalErrorException { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); // TODO: declaring method should probably have a generic type.. @SuppressWarnings("rawtypes") @@ -139,14 +137,6 @@ public class SearchParameter extends BaseQueryParameter { return retVal; } - public List> getDeclaredTypes() { - return Collections.unmodifiableList(myDeclaredTypes); - } - - public String getDescription() { - return myDescription; - } - /* * (non-Javadoc) * @@ -186,28 +176,17 @@ public class SearchParameter extends BaseQueryParameter { return myParamBinder.parse(theContext, getName(), theString); } - public void setChainlists(String[] theChainWhitelist, String[] theChainBlacklist) { - myQualifierWhitelist = new HashSet(theChainWhitelist.length); + public void setChainlists(String[] theChainWhitelist) { + myQualifierWhitelist = new HashSet<>(theChainWhitelist.length); myQualifierWhitelist.add(QUALIFIER_ANY_TYPE); - for (int i = 0; i < theChainWhitelist.length; i++) { - if (theChainWhitelist[i].equals(OptionalParam.ALLOW_CHAIN_ANY)) { + for (String chain : theChainWhitelist) { + if (chain.equals(OptionalParam.ALLOW_CHAIN_ANY)) { myQualifierWhitelist.add('.' + OptionalParam.ALLOW_CHAIN_ANY); - } else if (theChainWhitelist[i].equals(EMPTY_STRING)) { + } else if (chain.equals(EMPTY_STRING)) { myQualifierWhitelist.add("."); } else { - myQualifierWhitelist.add('.' + theChainWhitelist[i]); - } - } - - if (theChainBlacklist.length > 0) { - myQualifierBlacklist = new HashSet(theChainBlacklist.length); - for (String next : theChainBlacklist) { - if (next.equals(EMPTY_STRING)) { - myQualifierBlacklist.add(EMPTY_STRING); - } else { - myQualifierBlacklist.add('.' + next); - } + myQualifierWhitelist.add('.' + chain); } } } @@ -220,10 +199,6 @@ public class SearchParameter extends BaseQueryParameter { myDeclaredTypes = Arrays.asList(theTypes); } - public void setDescription(String theDescription) { - myDescription = theDescription; - } - public void setName(String name) { this.myName = name; } @@ -271,7 +246,7 @@ public class SearchParameter extends BaseQueryParameter { Set builtInQualifiers = ourParamQualifiers.get(typeEnum); if (builtInQualifiers != null) { if (myQualifierWhitelist != null) { - HashSet qualifierWhitelist = new HashSet(); + HashSet qualifierWhitelist = new HashSet<>(); qualifierWhitelist.addAll(myQualifierWhitelist); qualifierWhitelist.addAll(builtInQualifiers); myQualifierWhitelist = qualifierWhitelist; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirTesterConfig.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirTesterConfig.java index 1681064f6ba..8ef4c7f69ce 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirTesterConfig.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirTesterConfig.java @@ -1,5 +1,25 @@ package ca.uhn.hapi.fhir.docs; +/*- + * #%L + * HAPI FHIR - Docs + * %% + * Copyright (C) 2014 - 2019 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.to.FhirTesterMvcConfig; import ca.uhn.fhir.to.TesterConfig; diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/versions.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/versions.md index da2e4a43368..3f2e6726429 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/versions.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/introduction/versions.md @@ -9,7 +9,7 @@ this page. The following table shows the various versions of the HAPI FHIR library, and the versions of the FHIR standard that they support. Note that support for stable releases of FHIR are shown in GREEN and support for draft pre-release versions of FHIR are shown in YELLOW. Note also that after the release of the FHIR DSTU2 specification, the FHIR - standard itself stopped using the DSTUx naming scheme, in favour or naming new releases STUx or simply Rx. Because HAPI FHIR already had draft support for what was then called DSTU3 at this time, we did not update our naming conventions until R4 in order to avoid breaking existing users' code. From the perspective of a user of HAPI FHIR, consider the terms DSTU3 / STU3 / R3 to be interchangeable. + standard itself stopped using the DSTUx naming scheme, in favour or naming new releases STUx or simply Rx. Because HAPI FHIR already had draft support for what was then called DSTU3, we did not update our naming conventions until R4 in order to avoid breaking existing users' code. From the perspective of a user of HAPI FHIR, consider the terms DSTU3 / STU3 / R3 to be interchangeable. @@ -26,102 +26,122 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR - - - - + + - - - - - - - - - - - - - - - - - - - - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + @@ -136,124 +156,104 @@ Note also that after the release of the FHIR DSTU2 specification, the FHIR - + - + - - - + + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + + + + + - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - -
HAPI FHIR 1.1JDK60.0.820.5.0-5843HAPI FHIR 4.1.0JDK8
HAPI FHIR 1.2JDK60.0.820.5.0-5843
HAPI FHIR 1.3JDK60.0.82 1.0.21.4.03.0.24.0.14.1.0
1a7623d866
HAPI FHIR 1.4JDK60.0.82HAPI FHIR 4.0.0JDK8 1.0.21.3.0-76021.4.03.0.14.0.04.1.0
e0e3caf9ba
HAPI FHIR 1.5JDK60.0.82HAPI FHIR 3.8.0JDK8 1.0.21.4.0-81381.4.03.0.14.0.0
HAPI FHIR 1.6JDK60.0.82HAPI FHIR 3.7.0JDK8 1.0.21.4.0-86361.4.03.0.14.0.0
HAPI FHIR 2.0JDK60.0.82HAPI FHIR 3.6.0JDK8 1.0.21.6.0-96631.4.03.0.13.6.0
1202b2eed0f
HAPI FHIR 2.1JDK60.0.82HAPI FHIR 3.5.0JDK8 1.0.21.7.0-101291.4.03.0.13.4.0
13732
HAPI FHIR 2.2HAPI FHIR 3.4.0JDK81.0.21.4.03.0.13.4.0
13732
HAPI FHIR 3.3.0JDK71.0.21.4.03.0.13.2.0
13271
HAPI FHIR 3.2.0JDK71.0.21.4.03.0.13.2.0
12917
HAPI FHIR 3.1.0JDK71.0.21.4.03.0.13.1.0
12370
HAPI FHIR 3.0.0JDK71.0.21.4.03.0.13.1.0
12370
HAPI FHIR 2.5 JDK6 0.0.82 1.0.2 1.4.01.8.0-10528
HAPI FHIR 2.3JDK60.0.821.0.21.4.01.9.0-115013.0.1
HAPI FHIR 2.5HAPI FHIR 2.3 JDK6 0.0.82 1.0.2 1.4.03.0.11.9.0
11501
HAPI FHIR 3.0.0JDK7HAPI FHIR 2.2JDK60.0.82 1.0.2 1.4.03.0.13.1.0-123701.8.0
10528
HAPI FHIR 3.1.0JDK7HAPI FHIR 2.1JDK60.0.82 1.0.21.4.03.0.13.1.0-123701.7.0
10129
HAPI FHIR 3.2.0JDK7HAPI FHIR 2.0JDK60.0.82 1.0.21.4.03.0.13.2.0-129171.6.0
9663
HAPI FHIR 3.3.0JDK7HAPI FHIR 1.6JDK60.0.82 1.0.21.4.03.0.13.2.0-132711.4.0
8636
HAPI FHIR 3.4.0JDK8HAPI FHIR 1.5JDK60.0.82 1.0.21.4.03.0.13.4.0-137321.4.0
8138
HAPI FHIR 3.5.0JDK8HAPI FHIR 1.4JDK60.0.82 1.0.21.4.03.0.13.4.0-137321.3.0
7602
HAPI FHIR 3.6.0JDK8HAPI FHIR 1.3JDK60.0.82 1.0.21.4.03.0.13.6.0-1202b2eed0f
HAPI FHIR 3.7.0JDK8HAPI FHIR 1.2JDK60.0.820.5.0
5843
1.0.21.4.03.0.14.0.0
HAPI FHIR 3.8.0JDK8HAPI FHIR 1.1JDK60.0.820.5.0
5843
1.0.21.4.03.0.14.0.0
HAPI FHIR 4.0.0JDK8 1.0.21.4.03.0.14.0.04.1.0-e0e3caf9ba
HAPI FHIR 4.1.0JDK8 1.0.21.4.03.0.24.0.14.1.0-1a7623d866
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java index ba6f05098c7..89e1015eee3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; +import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.param.DateRangeParam; @@ -184,6 +185,9 @@ public class Search implements ICachedSearchDetails, Serializable { public void setFailureMessage(String theFailureMessage) { myFailureMessage = left(theFailureMessage, FAILURE_MESSAGE_LENGTH); + if (System.getProperty(SearchCoordinatorSvcImpl.UNIT_TEST_CAPTURE_STACK) != null) { + myFailureMessage = theFailureMessage; + } } public Long getId() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 4b8bba8f860..190c558d3cc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -904,7 +904,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { } if (System.getProperty(UNIT_TEST_CAPTURE_STACK) != null) { - failureMessage += "\n" + ExceptionUtils.getStackTrace(rootCause); + failureMessage += "\nStack\n" + ExceptionUtils.getStackTrace(rootCause); } mySearch.setFailureMessage(failureMessage); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index c1abe4308d1..315162bfc85 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -1824,8 +1824,8 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo if (isNotBlank(targetCode) && isNotBlank(targetCodeSystem)) { for (Iterator iter = nextElement.getConceptMapGroupElementTargets().iterator(); iter.hasNext(); ) { TermConceptMapGroupElementTarget next = iter.next(); - if (targetCodeSystem.equals(next.getSystem())) { - if (targetCode.equals(next.getCode())) { + if (StringUtils.equals(targetCodeSystem, next.getSystem())) { + if (StringUtils.equals(targetCode, next.getCode())) { continue; } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java index b1d61b6cbd3..df545aa87fe 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.search.reindex; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.BaseJpaTest; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoRegistry; @@ -24,6 +25,7 @@ import org.mockito.Mock; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.SliceImpl; import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; import java.util.*; import java.util.concurrent.CountDownLatch; @@ -68,6 +70,8 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest { private ResourceReindexJobEntity mySingleJob; @Mock private ISearchParamRegistry mySearchParamRegistry; + @Mock + private TransactionStatus myTxStatus; @Override protected FhirContext getContext() { @@ -94,6 +98,8 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest { mySvc.setTxManagerForUnitTest(myTxManager); mySvc.setSearchParamRegistryForUnitTest(mySearchParamRegistry); mySvc.start(); + + when(myTxManager.getTransaction(any())).thenReturn(myTxStatus); } @Test @@ -252,6 +258,37 @@ public class ResourceReindexingSvcImplTest extends BaseJpaTest { verifyNoMoreInteractions(myReindexJobDao); } + @Test + public void testReindexDeletedResource() { + mockNothingToExpunge(); + mockSingleReindexingJob("Patient"); + // Mock resource fetch + List values = Arrays.asList(0L); + when(myResourceTableDao.findIdsOfResourcesWithinUpdatedRangeOrderedFromOldest(myPageRequestCaptor.capture(), myTypeCaptor.capture(), myLowCaptor.capture(), myHighCaptor.capture())).thenReturn(new SliceImpl<>(values)); + // Mock fetching resources + long[] updatedTimes = new long[]{ + 10 * DateUtils.MILLIS_PER_DAY + }; + String[] resourceTypes = new String[]{ + "Patient", + }; + List resources = Arrays.asList( + new Patient().setId("Patient/0/_history/1") + ); + mockWhenResourceTableFindById(updatedTimes, resourceTypes); + when(myDaoRegistry.getResourceDao(eq("Patient"))).thenReturn(myResourceDao); + when(myDaoRegistry.getResourceDao(eq(Patient.class))).thenReturn(myResourceDao); + when(myDaoRegistry.getResourceDao(eq("Observation"))).thenReturn(myResourceDao); + when(myDaoRegistry.getResourceDao(eq(Observation.class))).thenReturn(myResourceDao); + when(myResourceDao.read(any(), any(), anyBoolean())).thenReturn(null); + + + int count = mySvc.forceReindexingPass(); + assertEquals(0, count); + + verify(myResourceTableDao, times(1)).updateIndexStatus(eq(0L), eq(BaseHapiFhirDao.INDEX_STATUS_INDEXING_FAILED)); + } + @Test public void testReindexThrowsError() { mockNothingToExpunge(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index cbca3b0e808..f727931f104 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -1,6 +1,13 @@ package ca.uhn.fhir.jpa.term; -import ca.uhn.fhir.jpa.entity.*; +import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.entity.TermValueSetConcept; +import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; +import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.term.custom.CustomTerminologySet; @@ -20,14 +27,25 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import javax.annotation.Nonnull; +import java.io.IOException; import java.util.List; import java.util.Optional; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.empty; -import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class ValueSetExpansionR4Test extends BaseTermR4Test { private static final Logger ourLog = LoggerFactory.getLogger(ValueSetExpansionR4Test.class); @@ -35,6 +53,39 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { @Mock private IValueSetConceptAccumulator myValueSetCodeAccumulator; + @Test + public void testDeletePreExpandedValueSet() throws IOException { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); + + CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId); + ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); + + ValueSet valueSet = myValueSetDao.read(myExtensionalVsId); + ourLog.info("ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet)); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + myCaptureQueriesListener.clear(); + + ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); + assertEquals(24, expandedValueSet.getExpansion().getContains().size()); + + runInTransaction(()->{ + assertEquals(24, myTermValueSetConceptDao.count()); + }); + + myValueSetDao.delete(valueSet.getIdElement()); + + runInTransaction(()->{ + assertEquals(0, myTermValueSetConceptDao.count()); + }); + + expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), myDaoConfig.getPreExpandValueSetsDefaultCount()); + assertEquals(24, expandedValueSet.getExpansion().getContains().size()); + } + @SuppressWarnings("SpellCheckingInspection") @Test public void testExpandTermValueSetAndChildren() throws Exception { @@ -141,7 +192,7 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { } - @Test + @Test public void testExpandExistingValueSetNotPreExpanded() throws Exception { loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); @@ -494,15 +545,16 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); ValueSet expandedValueSet = myTermSvc.expandValueSet(valueSet, myDaoConfig.getPreExpandValueSetsDefaultOffset(), 0); - ourLog.info("Expanded ValueSet:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); + String expanded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet); + ourLog.info("Expanded ValueSet:\n" + expanded); assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); - assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); - assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); - assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); - assertEquals("count", expandedValueSet.getExpansion().getParameter().get(1).getName()); - assertEquals(0, expandedValueSet.getExpansion().getParameter().get(1).getValueIntegerType().getValue().intValue()); + assertEquals(expanded, 2, expandedValueSet.getExpansion().getParameter().size()); + assertEquals(expanded, "offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); + assertEquals(expanded, 0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); + assertEquals(expanded, "count", expandedValueSet.getExpansion().getParameter().get(1).getName()); + assertEquals(expanded, 0, expandedValueSet.getExpansion().getParameter().get(1).getValueIntegerType().getValue().intValue()); assertFalse(expandedValueSet.getExpansion().hasContains()); } diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/SearchMethodBindingTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/SearchMethodBindingTest.java index 11bc0a42426..30c722bf5ed 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/SearchMethodBindingTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/SearchMethodBindingTest.java @@ -7,12 +7,16 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import com.google.common.collect.ImmutableMap; import org.hamcrest.Matchers; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; @@ -22,90 +26,114 @@ import static org.mockito.Mockito.when; public class SearchMethodBindingTest { - private static final TestResourceProvider TEST_RESOURCE_PROVIDER = new TestResourceProvider(); + private static final TestResourceProvider TEST_RESOURCE_PROVIDER = new TestResourceProvider(); + private static final Logger ourLog = LoggerFactory.getLogger(SearchMethodBindingTest.class); + private FhirContext fhirContext; - private FhirContext fhirContext; + @Before + public void setUp() { + fhirContext = mock(FhirContext.class); + RuntimeResourceDefinition definition = mock(RuntimeResourceDefinition.class); + when(definition.isBundle()).thenReturn(false); + when(fhirContext.getResourceDefinition(any(Class.class))).thenReturn(definition); + } - @Before - public void setUp() { - fhirContext = mock(FhirContext.class); - RuntimeResourceDefinition definition = mock(RuntimeResourceDefinition.class); - when(definition.isBundle()).thenReturn(false); - when(fhirContext.getResourceDefinition(any(Class.class))).thenReturn(definition); - } + @Test // fails + public void methodShouldNotMatchWhenUnderscoreQueryParameter() throws NoSuchMethodException { + Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))), + Matchers.is(false)); + Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))), + Matchers.is(false)); + Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))), + Matchers.is(false)); + } - @Test // fails - public void methodShouldNotMatchWhenUnderscoreQueryParameter() throws NoSuchMethodException { - Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))), - Matchers.is(false)); - Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))), - Matchers.is(false)); - Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))), - Matchers.is(false)); - } + @Test + public void methodShouldNotMatchWhenExtraQueryParameter() throws NoSuchMethodException { + Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))), + Matchers.is(false)); + Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))), + Matchers.is(false)); + Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))), + Matchers.is(false)); + } - @Test - public void methodShouldNotMatchWhenExtraQueryParameter() throws NoSuchMethodException { - Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))), - Matchers.is(false)); - Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))), - Matchers.is(false)); - Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))), - Matchers.is(false)); - } + @Test + public void methodMatchesOwnParams() throws NoSuchMethodException { + Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}))), + Matchers.is(true)); + Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "test", new String[]{"test"}))), + Matchers.is(true)); + Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_test", new String[]{"test"}))), + Matchers.is(true)); + } - @Test - public void methodMatchesOwnParams() throws NoSuchMethodException { - Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}))), - Matchers.is(true)); - Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "test", new String[]{"test"}))), - Matchers.is(true)); - Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod( - mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_test", new String[]{"test"}))), - Matchers.is(true)); - } + @Test + public void methodMatchesChainBlacklist() throws NoSuchMethodException { + SearchMethodBinding binding = getBinding("withChainBlacklist", ReferenceParam.class); + ourLog.info("Testing binding: {}", binding); + Assert.assertThat(binding.incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("refChainBlacklist.badChain", new String[]{"foo"}))), + Matchers.is(false)); + Assert.assertThat(binding.incomingServerRequestMatchesMethod( + mockSearchRequest(ImmutableMap.of("refChainBlacklist.goodChain", new String[]{"foo"}))), + Matchers.is(true)); + } - private SearchMethodBinding getBinding(String name, Class... parameters) throws NoSuchMethodException { - return new SearchMethodBinding(IBaseResource.class, - IBaseResource.class, - TestResourceProvider.class.getMethod(name, parameters), - fhirContext, - TEST_RESOURCE_PROVIDER); - } + private SearchMethodBinding getBinding(String name, Class... parameters) throws NoSuchMethodException { + return new SearchMethodBinding(IBaseResource.class, + IBaseResource.class, + TestResourceProvider.class.getMethod(name, parameters), + fhirContext, + TEST_RESOURCE_PROVIDER); + } - private RequestDetails mockSearchRequest(Map params) { - RequestDetails requestDetails = mock(RequestDetails.class); - when(requestDetails.getOperation()).thenReturn("_search"); - when(requestDetails.getRequestType()).thenReturn(RequestTypeEnum.GET); - when(requestDetails.getParameters()).thenReturn(params); - return requestDetails; - } + private RequestDetails mockSearchRequest(Map params) { + RequestDetails requestDetails = mock(RequestDetails.class); + when(requestDetails.getOperation()).thenReturn("_search"); + when(requestDetails.getRequestType()).thenReturn(RequestTypeEnum.GET); + when(requestDetails.getParameters()).thenReturn(params); - private static class TestResourceProvider { + when(requestDetails.getUnqualifiedToQualifiedNames()).thenAnswer(t -> { + RequestDetails rd = new ServletRequestDetails(null); + rd.setParameters(params); + return rd.getUnqualifiedToQualifiedNames(); + }); - @Search - public IBaseResource param(@RequiredParam(name = "param") String param) { - return null; - } + return requestDetails; + } - @Search - public IBaseResource paramAndTest(@RequiredParam(name = "param") String param, @OptionalParam(name = "test") String test) { - return null; - } + private static class TestResourceProvider { - @Search - public IBaseResource paramAndUnderscoreTest(@RequiredParam(name = "param") String param, @OptionalParam(name = "_test") String test) { - return null; - } + @Search + public IBaseResource param(@RequiredParam(name = "param") String param) { + return null; + } - } + @Search + public IBaseResource paramAndTest(@RequiredParam(name = "param") String param, @OptionalParam(name = "test") String test) { + return null; + } + + @Search + public IBaseResource paramAndUnderscoreTest(@RequiredParam(name = "param") String param, @OptionalParam(name = "_test") String test) { + return null; + } + + @Search + public IBaseResource withChainBlacklist(@OptionalParam(name = "refChainBlacklist", chainWhitelist = "goodChain", chainBlacklist = "badChain") ReferenceParam param) { + return null; + } + + } } diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java index 423d63a141f..be084ff6cf4 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2_1Test.java @@ -45,7 +45,7 @@ import java.util.*; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; public class XmlParserDstu2_1Test { diff --git a/hapi-fhir-structures-r4/src/test/resources/bundle-circ-ref.json b/hapi-fhir-structures-r4/src/test/resources/bundle-circ-ref.json new file mode 100644 index 00000000000..203738b377b --- /dev/null +++ b/hapi-fhir-structures-r4/src/test/resources/bundle-circ-ref.json @@ -0,0 +1,369 @@ +{ + "resourceType": "Bundle", + "id": "1f3236fa-2fab-453d-86e8-72387929257c", + "meta": { + "lastUpdated": "2019-04-04T17:53:00.051-04:00" + }, + "type": "searchset", + "total": 2, + "link": [ + { + "relation": "self", + "url": "https://uhndigitalfhir-dev.uhn.ca/uhn-fhir-service-v2/DiagnosticReport?_include=DiagnosticReport%3Aresult&_query=findDiagnosticReportsByPatientWithIssuedDate&code=urn%3Aoid%3A1.3.6.1.4.1.12201.102.5%7C491&issued=ge2000-08-07&subject%3Aidentifier=urn%3Aoid%3A2.16.840.1.113883.3.239.18.148%7C7001316" + } + ], + "entry": [ + { + "fullUrl": "https://uhndigitalfhir-dev.uhn.ca/uhn-fhir-service-v2/DiagnosticReport/5541279", + "resource": { + "resourceType": "DiagnosticReport", + "id": "5541279", + "meta": { + "profile": [ + "http://fhir.uhn.ca/Profile/diagnosticreport" + ] + }, + "contained": [ + { + "resourceType": "Observation", + "id": "1", + "meta": { + "profile": [ + "http://fhir.uhn.ca/Profile/observation" + ] + }, + "extension": [ + { + "url": "http://fhir.uhn.ca/Profile/observation#source_system_status", + "valueString": "F" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#lastUpdatedDateTime", + "valueInstant": "2007-07-30T14:48:41.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#createDateTime", + "valueInstant": "2007-07-30T14:48:41.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#sequence", + "valueString": "1" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#diagnosticReport", + "valueReference": { + "reference": "DiagnosticReport/5541279" + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "urn:oid:1.3.6.1.4.1.12201.102.6", + "code": "01005.1" + } + ], + "text": "Creatinine" + }, + "subject": { + "reference": "Patient/5556360" + }, + "issued": "2007-07-30T14:48:41.000-04:00", + "valueQuantity": { + "value": 72.0, + "unit": "umol/L" + }, + "interpretation": { + "coding": [ + { + "system": "urn:uhn:qcpr:interpretation_codes", + "code": "N" + } + ] + }, + "referenceRange": [ + { + "meaning": { + "text": "<=99" + } + } + ] + }, + { + "resourceType": "Observation", + "id": "2", + "meta": { + "profile": [ + "http://fhir.uhn.ca/Profile/observation" + ] + }, + "extension": [ + { + "url": "http://fhir.uhn.ca/Profile/observation#source_system_status", + "valueString": "F" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#lastUpdatedDateTime", + "valueInstant": "2007-07-30T14:48:41.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#createDateTime", + "valueInstant": "2007-07-30T14:48:41.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#sequence", + "valueString": "2" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#diagnosticReport", + "valueReference": { + "reference": "DiagnosticReport/5541279" + } + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "urn:oid:1.3.6.1.4.1.12201.102.6", + "code": "01439.2" + } + ], + "text": "Tech Comment" + }, + "subject": { + "reference": "Patient/5556360" + }, + "issued": "2007-07-30T14:48:41.000-04:00", + "valueString": "Req#T07-104306,.", + "interpretation": { + "coding": [ + { + "system": "urn:uhn:qcpr:interpretation_codes", + "code": "N" + } + ] + } + } + ], + "extension": [ + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#source_system_status", + "valueString": "F" + }, + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#lastUpdatedDateTime", + "valueInstant": "2007-07-30T14:48:41.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#createDateTime", + "valueInstant": "2007-07-30T14:48:41.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#terminologyCodesFromQuery", + "valueCoding": { + "system": "urn:oid:1.3.6.1.4.1.12201.102.5", + "code": "491" + } + } + ], + "identifier": [ + { + "system": "urn:uhn:qcpr:order_ids", + "value": "651621" + } + ], + "status": "final", + "code": { + "coding": [ + { + "system": "urn:oid:1.3.6.1.4.1.12201.102.5", + "code": "491" + } + ], + "text": "Creatinine, Plasma" + }, + "subject": { + "reference": "Patient/5556360" + }, + "encounter": { + "reference": "Encounter/5556361" + }, + "effectiveDateTime": "2007-04-26T14:36:00-04:00", + "issued": "2007-07-30T14:48:41.000-04:00", + "result": [ + { + "reference": "#1" + }, + { + "reference": "#2" + } + ] + } + }, + { + "fullUrl": "https://uhndigitalfhir-dev.uhn.ca/uhn-fhir-service-v2/DiagnosticReport/5777012", + "resource": { + "resourceType": "DiagnosticReport", + "id": "5777012", + "meta": { + "profile": [ + "http://fhir.uhn.ca/Profile/diagnosticreport" + ] + }, + "contained": [ + { + "resourceType": "Observation", + "id": "1", + "meta": { + "profile": [ + "http://fhir.uhn.ca/Profile/observation" + ] + }, + "extension": [ + { + "url": "http://fhir.uhn.ca/Profile/observation#source_system_status", + "valueString": "I" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#lastUpdatedDateTime", + "valueInstant": "2008-03-31T15:09:39.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#createDateTime", + "valueInstant": "2008-03-31T15:09:39.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#sequence", + "valueString": "1" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#diagnosticReport", + "valueReference": { + "reference": "DiagnosticReport/5777012" + } + } + ], + "status": "preliminary", + "code": { + "coding": [ + { + "system": "urn:oid:1.3.6.1.4.1.12201.102.6", + "code": "4.1" + } + ], + "text": "When" + }, + "subject": { + "reference": "Patient/5556360" + }, + "issued": "2008-03-31T15:09:39.000-04:00", + "valueString": "Monday, 31 March 08 1508 Rout" + }, + { + "resourceType": "Observation", + "id": "2", + "meta": { + "profile": [ + "http://fhir.uhn.ca/Profile/observation" + ] + }, + "extension": [ + { + "url": "http://fhir.uhn.ca/Profile/observation#source_system_status", + "valueString": "I" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#lastUpdatedDateTime", + "valueInstant": "2008-03-31T15:09:39.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#createDateTime", + "valueInstant": "2008-03-31T15:09:39.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#sequence", + "valueString": "2" + }, + { + "url": "http://fhir.uhn.ca/Profile/observation#diagnosticReport", + "valueReference": { + "reference": "DiagnosticReport/5777012" + } + } + ], + "status": "preliminary", + "code": { + "coding": [ + { + "system": "urn:oid:1.3.6.1.4.1.12201.102.6", + "code": "5.2" + } + ], + "text": "Specimen" + }, + "subject": { + "reference": "Patient/5556360" + }, + "issued": "2008-03-31T15:09:39.000-04:00", + "valueString": "Blood Plasma - Bio (Green)" + } + ], + "extension": [ + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#source_system_status", + "valueString": "I" + }, + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#lastUpdatedDateTime", + "valueInstant": "2008-03-31T15:09:39.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#createDateTime", + "valueInstant": "2008-03-31T15:09:39.000-04:00" + }, + { + "url": "http://fhir.uhn.ca/Profile/diagnosticreport#terminologyCodesFromQuery", + "valueCoding": { + "system": "urn:oid:1.3.6.1.4.1.12201.102.5", + "code": "491" + } + } + ], + "identifier": [ + { + "system": "urn:uhn:qcpr:order_ids", + "value": "814177" + } + ], + "status": "registered", + "code": { + "coding": [ + { + "system": "urn:oid:1.3.6.1.4.1.12201.102.5", + "code": "491" + } + ], + "text": "Creatinine, Plasma" + }, + "subject": { + "reference": "Patient/5556360" + }, + "encounter": { + "reference": "Encounter/5606097" + }, + "effectiveDateTime": "2008-03-31T15:09:00-04:00", + "issued": "2008-03-31T15:09:39.000-04:00", + "result": [ + { + "reference": "#1" + }, + { + "reference": "#2" + } + ] + } + } + ] +}