diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java index c6068e314ee..fcd4b575262 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/IdParam.java @@ -28,5 +28,11 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface IdParam { - // just a marker + + /** + * For {@link Operation extended operations}, any parameter with this value set to true + * (default is false) will also be invoked if the operation is invoked against the resource type. + */ + boolean optional() default false; + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index f0e13e587aa..5b95a4e5c77 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -50,11 +50,13 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import ca.uhn.fhir.context.BaseRuntimeChildDatatypeDefinition; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.IRuntimeDatatypeDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IQueryParameterType; @@ -1496,8 +1498,16 @@ public class GenericClient extends BaseClient implements IGenericClient { parameterElem.getChildByName("name").getMutator().setValue(parameter, name); if (theValue instanceof IBaseDatatype) { - String childElementName = "value" + StringUtils.capitalize(myContext.getElementDefinition(theValue.getClass()).getName()); - parameterElem.getChildByName(childElementName).getMutator().setValue(parameter, theValue); + BaseRuntimeElementDefinition datatypeDef = myContext.getElementDefinition(theValue.getClass()); + if (datatypeDef instanceof IRuntimeDatatypeDefinition) { + Class profileOf = ((IRuntimeDatatypeDefinition) datatypeDef).getProfileOf(); + if (profileOf != null) { + datatypeDef = myContext.getElementDefinition(profileOf); + } + } + String childElementName = "value" + StringUtils.capitalize(datatypeDef.getName()); + BaseRuntimeChildDefinition childByName = parameterElem.getChildByName(childElementName); + childByName.getMutator().setValue(parameter, theValue); } else if (theValue instanceof IBaseResource) { parameterElem.getChildByName("resource").getMutator().setValue(parameter, theValue); } else { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java index 153c1cead64..5a3ba9f65d0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/OperationMethodBinding.java @@ -22,11 +22,12 @@ package ca.uhn.fhir.rest.method; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -43,6 +44,7 @@ import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; +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.RequestTypeEnum; @@ -67,6 +69,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { private final RestOperationTypeEnum myOtherOperatiopnType; private List myReturnParams; private final ReturnTypeEnum myReturnType; + private boolean myCanOperateAtTypeLevel; protected OperationMethodBinding(Class theReturnResourceType, Class theReturnTypeFromRp, Method theMethod, FhirContext theContext, Object theProvider, boolean theIdempotent, String theOperationName, Class theOperationType, OperationParam[] theReturnParams) { @@ -74,6 +77,16 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { myIdempotent = theIdempotent; myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod); + if (myIdParamIndex != null) { + for (Annotation next : theMethod.getParameterAnnotations()[myIdParamIndex]) { + if (next instanceof IdParam) { + myCanOperateAtTypeLevel = ((IdParam) next).optional() == true; + } + } + } else { + myCanOperateAtTypeLevel = true; + } + Description description = theMethod.getAnnotation(Description.class); if (description != null) { myDescription = description.formalDefinition(); @@ -195,12 +208,22 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { return false; } - boolean requestHasId = theRequest.getId() != null; - if (requestHasId != (myIdParamIndex != null)) { + if (!myName.equals(theRequest.getOperation())) { return false; } + + boolean requestHasId = theRequest.getId() != null; + if (requestHasId) { + if (isCanOperateAtInstanceLevel() == false) { + return false; + } + } else { + if (myCanOperateAtTypeLevel == false) { + return false; + } + } - return myName.equals(theRequest.getOperation()); + return true; } @Override @@ -289,7 +312,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding { FhirTerser t = theContext.newTerser(); List parameters = t.getValues(theInput, "Parameters.parameter"); - Map> params = new HashMap>(); + Map> params = new LinkedHashMap>(); for (Object nextParameter : parameters) { IPrimitiveType nextNameDt = (IPrimitiveType) t.getSingleValueOrNull((IBase) nextParameter, "name"); if (nextNameDt == null || nextNameDt.isEmpty()) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 049589ea631..3cc97b102b8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -162,7 +162,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH private Class myResourceType; private String mySecondaryPrimaryKeyParamName; - @Autowired + @Autowired() private ISearchResultDao mySearchResultDao; private Set addPredicateComposite(RuntimeSearchParam theParamDef, Set thePids, List theNextAnd) { @@ -1784,11 +1784,11 @@ public abstract class BaseHapiFhirResourceDao extends BaseH List results = q.getResultList(); for (ResourceLink resourceLink : results) { if (theReverseMode) { - if (theEverythingModeEnum.isEncounter()) { - if (resourceLink.getSourcePath().equals("Encounter.subject") || resourceLink.getSourcePath().equals("Encounter.patient")) { - nextRoundOmit.add(resourceLink.getSourceResourcePid()); - } - } +// if (theEverythingModeEnum.isEncounter()) { +// if (resourceLink.getSourcePath().equals("Encounter.subject") || resourceLink.getSourcePath().equals("Encounter.patient")) { +// nextRoundOmit.add(resourceLink.getSourceResourcePid()); +// } +// } pidsToInclude.add(resourceLink.getSourceResourcePid()); } else { pidsToInclude.add(resourceLink.getTargetResourcePid()); @@ -2154,8 +2154,8 @@ public abstract class BaseHapiFhirResourceDao extends BaseH CriteriaQuery cq = builder.createTupleQuery(); Root from = cq.from(ResourceTable.class); List predicates = new ArrayList(); - if (theParams.get(BaseResource.RES_ID) != null) { - StringParam idParm = (StringParam) theParams.get(BaseResource.RES_ID).get(0).get(0); + if (theParams.get(BaseResource.SP_RES_ID) != null) { + StringParam idParm = (StringParam) theParams.get(BaseResource.SP_RES_ID).get(0).get(0); predicates.add(builder.equal(from.get("myId"), idParm.getValue())); } predicates.add(builder.equal(from.get("myResourceType"), myResourceName)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java index 531dee1daa0..8546e221b50 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.dao.data; +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2015 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 org.springframework.data.jpa.repository.JpaRepository; import ca.uhn.fhir.jpa.entity.ResourceTable; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java index 33d001485c8..01db2729891 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.dao.data; +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2015 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 org.springframework.data.jpa.repository.JpaRepository; import ca.uhn.fhir.jpa.entity.SearchResult; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java index 459e608a92a..c2ec3dfd29c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/ResourceLink.java @@ -37,8 +37,6 @@ import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -import com.phloc.commons.annotations.ContainsSoftMigration; - @Entity @Table(name = "HFJ_RES_LINK" , indexes= { @Index(name="IDX_RL_TPATHRES", columnList= "SRC_PATH,TARGET_RESOURCE_ID"), diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java index dfaf108450d..f97e3054695 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderValueSetDstu2.java @@ -24,6 +24,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.BooleanUtils; + import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; @@ -38,65 +40,63 @@ import ca.uhn.fhir.model.primitive.UriDt; 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.server.exceptions.InvalidRequestException; public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDstu2 { //@formatter:off @Operation(name = "$expand", idempotent = true) - public ValueSet everything( + public ValueSet expand( HttpServletRequest theServletRequest, - - @IdParam IdDt theId, - + @IdParam(optional=true) IdDt theId, + @OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet, + @OperationParam(name="identifier", min=0, max=1) UriDt theIdentifier, @OperationParam(name = "filter", min=0, max=1) StringDt theFilter) { //@formatter:on + boolean haveId = theId != null && theId.hasIdPart(); + boolean haveIdentifier = theIdentifier != null && isNotBlank(theIdentifier.getValue()); + boolean haveValueSet = theValueSet != null && theValueSet.isEmpty() == false; + + if (!haveId && !haveIdentifier && !haveValueSet) { + throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request"); + } + + if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) { + throw new InvalidRequestException("$expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options."); + } + startRequest(theServletRequest); try { IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); - return dao.expand(theId, toFilterString(theFilter)); + if (haveId) { + return dao.expand(theId, toFilterString(theFilter)); + } else if (haveIdentifier) { + return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter)); + } else { + return dao.expand(theValueSet, toFilterString(theFilter)); + } + } finally { endRequest(theServletRequest); } } - //@formatter:off - @Operation(name = "$expand", idempotent = true) - public ValueSet everything( - HttpServletRequest theServletRequest, - @OperationParam(name="identifier", min=1, max=1) UriDt theIdentifier, - - @OperationParam(name = "filter", min=0, max=1) StringDt theFilter) { - //@formatter:on - - startRequest(theServletRequest); - try { - IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); - return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter)); - } finally { - endRequest(theServletRequest); + private static boolean moreThanOneTrue(boolean... theBooleans) { + boolean haveOne = false; + for (boolean next : theBooleans) { + if (next) { + if (haveOne) { + return true; + } else { + haveOne = true; + } + } } + return false; } - //@formatter:off - @Operation(name = "$expand", idempotent = true) - public ValueSet everything( - HttpServletRequest theServletRequest, - - @OperationParam(name="valueSet", min=1, max=1) ValueSet theValueSet, - - @OperationParam(name = "filter", min=0, max=1) StringDt theFilter) { - //@formatter:on - - startRequest(theServletRequest); - try { - IFhirResourceDaoValueSet dao = (IFhirResourceDaoValueSet) getDao(); - return dao.expand(theValueSet, toFilterString(theFilter)); - } finally { - endRequest(theServletRequest); - } - } private String toFilterString(StringDt theFilter) { return theFilter != null ? theFilter.getValue() : null; @@ -108,15 +108,15 @@ public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDst @OperationParam(name="message", type=StringDt.class), @OperationParam(name="display", type=StringDt.class) }) - public Parameters everything( + public Parameters validateCode( HttpServletRequest theServletRequest, - @IdParam IdDt theId, - @OperationParam(name="identifier") UriDt theValueSetIdentifier, - @OperationParam(name="code") CodeDt theCode, - @OperationParam(name="system") UriDt theSystem, - @OperationParam(name="display") StringDt theDisplay, - @OperationParam(name="coding") CodingDt theCoding, - @OperationParam(name="codeableConcept") CodeableConceptDt theCodeableConcept + @IdParam(optional=true) IdDt theId, + @OperationParam(name="identifier", min=0, max=1) UriDt theValueSetIdentifier, + @OperationParam(name="code", min=0, max=1) CodeDt theCode, + @OperationParam(name="system", min=0, max=1) UriDt theSystem, + @OperationParam(name="display", min=0, max=1) StringDt theDisplay, + @OperationParam(name="coding", min=0, max=1) CodingDt theCoding, + @OperationParam(name="codeableConcept", min=0, max=1) CodeableConceptDt theCodeableConcept ) { //@formatter:on diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java index 96bb355fc3a..c2a4acc8257 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java @@ -678,6 +678,8 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test { @Test public void testTransactionDeleteMatchUrlWithTwoMatch() { + myDaoConfig.setAllowMultipleDelete(false); + String methodName = "testTransactionDeleteMatchUrlWithTwoMatch"; Patient p = new Patient(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index 1089cb26a3f..a2b469cfe35 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -771,6 +771,8 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test { obs.getEncounter().setReference(encId); IIdType obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless(); + ourLog.info("IDs: EncU:" + encUId.getIdPart() + " Enc:" + encId.getIdPart() + " " + patientId.toUnqualifiedVersionless()); + Parameters output = ourClient.operation().onInstance(encId).named("everything").withNoParameters(Parameters.class).execute(); ca.uhn.fhir.model.dstu2.resource.Bundle b = (ca.uhn.fhir.model.dstu2.resource.Bundle) output.getParameterFirstRep().getResource(); List ids = toUnqualifiedVersionlessIds(b); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java index e89eeeadd98..2ceff26bd2a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java @@ -3,7 +3,9 @@ package ca.uhn.fhir.jpa.provider; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; import java.io.IOException; @@ -14,8 +16,11 @@ import org.springframework.transaction.annotation.Transactional; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.ValueSet; +import ca.uhn.fhir.model.primitive.BooleanDt; +import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.UriDt; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2Test { @@ -30,6 +35,26 @@ public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2 myExtensionalVsId = myValueSetDao.create(upload).getId().toUnqualifiedVersionless(); } + @Test + public void testValidateCodeOperationByCodeAndSystemBad() { + //@formatter:off + Parameters respParam = ourClient + .operation() + .onInstance(myExtensionalVsId) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeDt("8495-4")) + .andParameter("system", new UriDt("http://loinc.org")) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals(new BooleanDt(true), respParam.getParameter().get(0).getValue()); + } + + + @Test public void testExpandById() throws IOException { //@formatter:off @@ -112,7 +137,7 @@ public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2 //@formatter:off Parameters respParam = ourClient .operation() - .onInstance(myExtensionalVsId) + .onType(ValueSet.class) .named("expand") .withParameter(Parameters.class, "identifier", new UriDt("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) .andParameter("filter", new StringDt("11378")) @@ -138,7 +163,7 @@ public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2 //@formatter:off Parameters respParam = ourClient .operation() - .onInstance(myExtensionalVsId) + .onType(ValueSet.class) .named("expand") .withParameter(Parameters.class, "valueSet", toExpand) .andParameter("filter", new StringDt("11378")) @@ -157,5 +182,54 @@ public class ResourceProviderDstu2ValueSetTest extends BaseResourceProviderDstu2 assertThat(resp, not(containsString(""))); } + @Test + public void testExpandInvalidParams() throws IOException { + //@formatter:off + try { + ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withNoParameters(Parameters.class) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: $expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request", e.getMessage()); + } + //@formatter:on + + //@formatter:off + try { + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml"); + ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("identifier", new UriDt("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage()); + } + //@formatter:on + + //@formatter:off + try { + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-2.xml"); + ourClient + .operation() + .onInstance(myExtensionalVsId) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("identifier", new UriDt("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2")) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage()); + } + //@formatter:on + + } } diff --git a/hapi-fhir-jpaserver-example/src/main/resources/META-INF/fhirtest_persistence.xml b/hapi-fhir-jpaserver-example/src/main/resources/META-INF/fhirtest_persistence.xml index 29aef2bb642..3cf4e80f5f0 100644 --- a/hapi-fhir-jpaserver-example/src/main/resources/META-INF/fhirtest_persistence.xml +++ b/hapi-fhir-jpaserver-example/src/main/resources/META-INF/fhirtest_persistence.xml @@ -22,6 +22,8 @@ ca.uhn.fhir.jpa.entity.ResourceTag ca.uhn.fhir.jpa.entity.SubscriptionTable ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource + ca.uhn.fhir.jpa.entity.Search + ca.uhn.fhir.jpa.entity.SearchResult ca.uhn.fhir.jpa.entity.TagDefinition true diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/META-INF/fhirtest_persistence.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/META-INF/fhirtest_persistence.xml index ed759870e4e..15101bffc69 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/META-INF/fhirtest_persistence.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/resources/META-INF/fhirtest_persistence.xml @@ -23,6 +23,8 @@ ca.uhn.fhir.jpa.entity.SubscriptionTable ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource ca.uhn.fhir.jpa.entity.TagDefinition + ca.uhn.fhir.jpa.entity.Search + ca.uhn.fhir.jpa.entity.SearchResult true diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index c7b027c2fde..9a106f8d5aa 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -22,6 +22,7 @@ import org.apache.http.Header; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; +import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpUriRequest; @@ -52,6 +53,7 @@ import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; @@ -70,6 +72,8 @@ import ca.uhn.fhir.rest.server.EncodingEnum; public class GenericClientDstu2Test { private static FhirContext ourCtx; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu2Test.class); + private HttpClient myHttpClient; private HttpResponse myHttpResponse; @@ -348,7 +352,7 @@ public class GenericClientDstu2Test { assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue()); assertThat(extractBody(capt, idx), containsString("")); assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString()); - assertEquals("http://example.com/fhir/Patient?name=http://foo%7Cbar", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("http://example.com/fhir/Patient?name=http%3A//foo%7Cbar", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_IF_NONE_EXIST).getValue()); assertEquals("POST", capt.getAllValues().get(idx).getRequestLine().getMethod()); idx++; @@ -1089,6 +1093,60 @@ public class GenericClientDstu2Test { //@formatter:on } + @Test + public void testOperationWithProfiledDatatypeParam() throws IOException, Exception { + IParser p = ourCtx.newXmlParser(); + + Parameters outParams = new Parameters(); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT1")); + outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); + final String respString = p.encodeResourceToString(outParams); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + int idx = 0; + + //@formatter:off + client + .operation() + .onInstance(new IdDt("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeDt("8495-4")) + .andParameter("system", new UriDt("http://loinc.org")) + .useHttpGet() + .execute(); + //@formatter:off + + assertEquals("http://example.com/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", capt.getAllValues().get(idx).getURI().toASCIIString()); + + //@formatter:off + idx++; + client + .operation() + .onInstance(new IdDt("http://foo/Patient/1")) + .named("validate-code") + .withParameter(Parameters.class, "code", new CodeDt("8495-4")) + .andParameter("system", new UriDt("http://loinc.org")) + .execute(); + //@formatter:off + + assertEquals("http://example.com/fhir/Patient/1/$validate-code", capt.getAllValues().get(idx).getURI().toASCIIString()); + ourLog.info(extractBody(capt, idx)); + assertEquals("",extractBody(capt, idx)); + + } + @Test public void testOperationWithListOfParameterResponse() throws Exception { IParser p = ourCtx.newXmlParser(); @@ -1556,6 +1614,85 @@ public class GenericClientDstu2Test { } + @Test + public void testSearchByUrl() throws Exception { + + final String msg = getPatientFeedWithOneResult(); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); + } + }); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + int idx = 0; + + //@formatter:off + ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() + .byUrl("http://foo?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://foo?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); + assertNotNull(response); + idx++; + + //@formatter:off + response = client.search() + .byUrl("Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); + assertNotNull(response); + idx++; + + //@formatter:off + response = client.search() + .byUrl("/Patient?name=http://foo|bar") + .encodedJson() + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); + assertNotNull(response); + idx++; + + //@formatter:off + response = client.search() + .byUrl("Patient") + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString()); + assertNotNull(response); + idx++; + + //@formatter:off + response = client.search() + .byUrl("Patient?") + .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) + .execute(); + //@formatter:on + assertEquals("http://example.com/fhir/Patient?", capt.getAllValues().get(idx).getURI().toString()); + assertNotNull(response); + idx++; + + try { + client.search().byUrl("foo/bar?test=1"); + } catch (IllegalArgumentException e) { + assertEquals("Search URL must be either a complete URL starting with http: or https:, or a relative FHIR URL in the form [ResourceType]?[Params]", e.getMessage()); + } + } + /** * See #191 */ @@ -1692,84 +1829,6 @@ public class GenericClientDstu2Test { } - @Test - public void testSearchByUrl() throws Exception { - - final String msg = getPatientFeedWithOneResult(); - - ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); - when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); - when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); - when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); - when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { - @Override - public InputStream answer(InvocationOnMock theInvocation) throws Throwable { - return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")); - }}); - - IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); - int idx = 0; - - //@formatter:off - ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() - .byUrl("http://foo?name=http://foo|bar") - .encodedJson() - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); - //@formatter:on - assertEquals("http://foo?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); - assertNotNull(response); - idx++; - - //@formatter:off - response = client.search() - .byUrl("Patient?name=http://foo|bar") - .encodedJson() - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); - //@formatter:on - assertEquals("http://example.com/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); - assertNotNull(response); - idx++; - - //@formatter:off - response = client.search() - .byUrl("/Patient?name=http://foo|bar") - .encodedJson() - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); - //@formatter:on - assertEquals("http://example.com/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", capt.getAllValues().get(idx).getURI().toString()); - assertNotNull(response); - idx++; - - //@formatter:off - response = client.search() - .byUrl("Patient") - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); - //@formatter:on - assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString()); - assertNotNull(response); - idx++; - - //@formatter:off - response = client.search() - .byUrl("Patient?") - .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) - .execute(); - //@formatter:on - assertEquals("http://example.com/fhir/Patient?", capt.getAllValues().get(idx).getURI().toString()); - assertNotNull(response); - idx++; - - try { - client.search().byUrl("foo/bar?test=1"); - } catch (IllegalArgumentException e) { - assertEquals("Search URL must be either a complete URL starting with http: or https:, or a relative FHIR URL in the form [ResourceType]?[Params]", e.getMessage()); - } - } - @Test public void testSearchWithSummaryParam() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java index b5bb48f18df..a755c01be51 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerTest.java @@ -274,6 +274,56 @@ public class OperationServerTest { assertEquals("RET1", resp.getParameter().get(0).getName()); } + @Test + public void testOperationOnInstanceAndType_Instance() throws Exception { + Parameters p = new Parameters(); + p.addParameter().setName("PARAM1").setValue(new StringDt("PARAM1val")); + p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); + String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); + + HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE_OR_TYPE"); + httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + HttpResponse status = ourClient.execute(httpPost); + + assertEquals(200, status.getStatusLine().getStatusCode()); + String response = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + + assertEquals("PARAM1val", ourLastParam1.getValue()); + assertEquals(true, ourLastParam2.getActive().booleanValue()); + assertEquals("123", ourLastId.getIdPart()); + assertEquals("$OP_INSTANCE_OR_TYPE", ourLastMethod); + + Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response); + assertEquals("RET1", resp.getParameter().get(0).getName()); + + } + + @Test + public void testOperationOnInstanceAndType_Type() throws Exception { + Parameters p = new Parameters(); + p.addParameter().setName("PARAM1").setValue(new StringDt("PARAM1val")); + p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); + String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); + + HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE_OR_TYPE"); + httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + CloseableHttpResponse status = ourClient.execute(httpPost); + + assertEquals(200, status.getStatusLine().getStatusCode()); + String response = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + + assertEquals("PARAM1val", ourLastParam1.getValue()); + assertEquals(true, ourLastParam2.getActive().booleanValue()); + assertEquals(null, ourLastId); + assertEquals("$OP_INSTANCE_OR_TYPE", ourLastMethod); + + Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response); + assertEquals("RET1", resp.getParameter().get(0).getName()); + } + + @Test public void testOperationOnInstance() throws Exception { Parameters p = new Parameters(); @@ -296,6 +346,20 @@ public class OperationServerTest { Parameters resp = ourCtx.newXmlParser().parseResource(Parameters.class, response); assertEquals("RET1", resp.getParameter().get(0).getName()); + + /* + * Against type should fail + */ + + httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE"); + httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + status = ourClient.execute(httpPost); + + response = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(response); + assertEquals(400, status.getStatusLine().getStatusCode()); + } @Test @@ -611,6 +675,25 @@ public class OperationServerTest { return retVal; } + //@formatter:off + @Operation(name="$OP_INSTANCE_OR_TYPE") + public Parameters opInstanceOrType( + @IdParam(optional=true) IdDt theId, + @OperationParam(name="PARAM1") StringDt theParam1, + @OperationParam(name="PARAM2") Patient theParam2 + ) { + //@formatter:on + + ourLastMethod = "$OP_INSTANCE_OR_TYPE"; + ourLastId = theId; + ourLastParam1 = theParam1; + ourLastParam2 = theParam2; + + Parameters retVal = new Parameters(); + retVal.addParameter().setName("RET1").setValue(new StringDt("RETVAL1")); + return retVal; + } + } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java index e481fc4b9c2..beda80735ac 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java @@ -7,13 +7,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -28,6 +33,7 @@ import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu2.composite.IdentifierDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.InstantDt; @@ -138,6 +144,48 @@ public class SearchDstu2Test { assertEquals("2002", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(1).getValueAsString()); } + + @Test + public void testSearchByPost() throws Exception { + HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); + httpGet.setEntity(entity); + + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals("searchDateAndList", ourLastMethod); + assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().size()); + assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); + assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().get(1).getValuesAsQueryTokens().size()); + assertEquals("2001", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValueAsString()); + assertEquals("2002", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(1).getValueAsString()); + assertThat(responseContent, containsString("SYSTEM")); + } + + @Test + public void testSearchByPostWithBodyAndUrlParams() throws Exception { + + HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=json"); + StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); + httpGet.setEntity(entity); + + CloseableHttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals("searchDateAndList", ourLastMethod); + assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().size()); + assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); + assertEquals(2, ourLastDateAndList.getValuesAsQueryTokens().get(1).getValuesAsQueryTokens().size()); + assertEquals("2001", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValueAsString()); + assertEquals("2002", ourLastDateAndList.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(1).getValueAsString()); + assertThat(responseContent, containsString(":\"SYSTEM\"")); + + } + + @Test public void testSearchBlacklist01Failing() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchBlacklist01&ref.black1=value"); @@ -292,7 +340,11 @@ public class SearchDstu2Test { @RequiredParam(name = "searchDateAndList") DateAndListParam theParam) { ourLastMethod = "searchDateAndList"; ourLastDateAndList = theParam; - return Collections.emptyList(); + ArrayList retVal = new ArrayList(); + Patient patient = new Patient(); + patient.setId("1"); + retVal.add(patient.addIdentifier(new IdentifierDt("SYSTEM", "CODE"))); + return retVal; } //@formatter:on diff --git a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm index d80186fdc79..8d3998ad76b 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/jpa_spring_beans.vm @@ -23,9 +23,10 @@ + + #if ( ${versionCapitalized} == 'Dstu2' ) - diff --git a/hapi-tinder-plugin/src/main/resources/vm/valueset.vm b/hapi-tinder-plugin/src/main/resources/vm/valueset.vm index bb28287f96d..0e338100456 100644 --- a/hapi-tinder-plugin/src/main/resources/vm/valueset.vm +++ b/hapi-tinder-plugin/src/main/resources/vm/valueset.vm @@ -69,7 +69,7 @@ public enum ${valueSet.className} { /** * Returns the enumerated value associated with this code */ - public ${valueSet.className} forCode(String theCode) { + public static ${valueSet.className} forCode(String theCode) { ${valueSet.className} retVal = CODE_TO_ENUM.get(theCode); return retVal; } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 633a29206b2..3b8df10ab81 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -155,6 +155,13 @@ Refactor JPA $everything operations so that they perform better + + Server operation methods can now declare the + ID optional, via + @IdParam(optional=true) + meaning that the same operation can also be invoked + at the type level. +