Use correct parameter name for expand operation

This commit is contained in:
James 2017-10-02 19:21:52 -04:00
parent 56a71f9222
commit 4d1ab2734f
5 changed files with 492 additions and 436 deletions

View File

@ -20,33 +20,40 @@ package ca.uhn.fhir.jpa.provider.dstu3;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
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 ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.dstu3.model.*;
import javax.servlet.http.HttpServletRequest;
import org.hl7.fhir.dstu3.model.*;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDstu3<ValueSet> {
//@formatter:off
@Operation(name = "$expand", idempotent = true)
public ValueSet expand(
HttpServletRequest theServletRequest,
@IdParam(optional=true) IdType theId,
@OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet,
@OperationParam(name="identifier", min=0, max=1) UriType theIdentifier,
@OperationParam(name = "filter", min=0, max=1) StringType theFilter,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
// Note: url is correct and identifier is not, but identifier was only added as
// of 3.1.0 so we'll leave url for now. See: https://groups.google.com/d/msgid/hapi-fhir/CAN2Cfy8kW%2BAOkgC6VjPsU3gRCpExCNZBmJdi-k5R_TWeyWH4tA%40mail.gmail.com?utm_medium=email&utm_source=footer
@OperationParam(name = "url", min = 0, max = 1) UriType theUrl,
@OperationParam(name = "identifier", min = 0, max = 1) UriType theIdentifier,
@OperationParam(name = "filter", min = 0, max = 1) StringType theFilter,
RequestDetails theRequestDetails) {
//@formatter:on
boolean haveId = theId != null && theId.hasIdPart();
boolean haveIdentifier = theIdentifier != null && isNotBlank(theIdentifier.getValue());
UriType url = theIdentifier;
if (theUrl != null && isNotBlank(theUrl.getValue())) {
url = theUrl;
}
boolean haveIdentifier = url != null && isNotBlank(url.getValue());
boolean haveValueSet = theValueSet != null && theValueSet.isEmpty() == false;
if (!haveId && !haveIdentifier && !haveValueSet) {
@ -63,7 +70,7 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst
if (haveId) {
return dao.expand(theId, toFilterString(theFilter), theRequestDetails);
} else if (haveIdentifier) {
return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter));
return dao.expandByIdentifier(url.getValue(), toFilterString(theFilter));
} else {
return dao.expand(theValueSet, toFilterString(theFilter));
}
@ -79,30 +86,34 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst
}
//@formatter:off
@SuppressWarnings("unchecked")
@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)
@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="identifier", min=0, max=1) UriType theValueSetIdentifier,
@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="coding", min=0, max=1) Coding theCoding,
@OperationParam(name="codeableConcept", min=0, max=1) CodeableConcept theCodeableConcept,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "identifier", min = 0, max = 1) UriType theValueSetIdentifier,
@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 = "coding", min = 0, max = 1) Coding theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept,
RequestDetails theRequestDetails
) {
//@formatter:on
UriType url = theValueSetIdentifier;
if (theValueSetUrl != null && isNotBlank(theValueSetUrl.getValue())) {
url = theValueSetUrl;
}
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
ValidateCodeResult result = dao.validateCode(url, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
Parameters retVal = new Parameters();
retVal.addParameter().setName("result").setValue(new BooleanType(result.isResult()));
if (isNotBlank(result.getMessage())) {
@ -118,7 +129,6 @@ public class BaseJpaResourceProviderValueSetDstu3 extends JpaResourceProviderDst
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {

View File

@ -20,41 +20,40 @@ package ca.uhn.fhir.jpa.provider.r4;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
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 ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.r4.model.*;
import javax.servlet.http.HttpServletRequest;
import org.hl7.fhir.r4.model.*;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4<ValueSet> {
//@formatter:off
@Operation(name = "$expand", idempotent = true)
public ValueSet expand(
HttpServletRequest theServletRequest,
@IdParam(optional=true) IdType theId,
@OperationParam(name="valueSet", min=0, max=1) ValueSet theValueSet,
@OperationParam(name="identifier", min=0, max=1) UriType theIdentifier,
@OperationParam(name = "filter", min=0, max=1) StringType theFilter,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
@OperationParam(name = "url", min = 0, max = 1) UriType theUrl,
@OperationParam(name = "filter", min = 0, max = 1) StringType theFilter,
RequestDetails theRequestDetails) {
//@formatter:on
boolean haveId = theId != null && theId.hasIdPart();
boolean haveIdentifier = theIdentifier != null && isNotBlank(theIdentifier.getValue());
boolean haveIdentifier = theUrl != null && isNotBlank(theUrl.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");
throw new InvalidRequestException("$expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request");
}
if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.");
throw new InvalidRequestException("$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.");
}
startRequest(theServletRequest);
@ -63,7 +62,7 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4<Val
if (haveId) {
return dao.expand(theId, toFilterString(theFilter), theRequestDetails);
} else if (haveIdentifier) {
return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter));
return dao.expandByIdentifier(theUrl.getValue(), toFilterString(theFilter));
} else {
return dao.expand(theValueSet, toFilterString(theFilter));
}
@ -79,30 +78,28 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4<Val
}
//@formatter:off
@SuppressWarnings("unchecked")
@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)
@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="identifier", min=0, max=1) UriType theValueSetIdentifier,
@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="coding", min=0, max=1) Coding theCoding,
@OperationParam(name="codeableConcept", min=0, max=1) CodeableConcept theCodeableConcept,
@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 = "coding", min = 0, max = 1) Coding theCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept,
RequestDetails theRequestDetails
) {
//@formatter:on
startRequest(theServletRequest);
try {
IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> dao = (IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept>) getDao();
ValidateCodeResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
ValidateCodeResult result = dao.validateCode(theValueSetUrl, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
Parameters retVal = new Parameters();
retVal.addParameter().setName("result").setValue(new BooleanType(result.isResult()));
if (isNotBlank(result.getMessage())) {
@ -118,7 +115,6 @@ public class BaseJpaResourceProviderValueSetR4 extends JpaResourceProviderR4<Val
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {

View File

@ -1,13 +1,15 @@
package ca.uhn.fhir.jpa.provider.dstu3;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_VALUE_SET;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
@ -19,17 +21,18 @@ import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM;
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_VALUE_SET;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3Test {
@ -48,31 +51,6 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless();
}
/**
* #516
*/
@Test
public void testInvalidFilter() throws Exception {
String string = IOUtils.toString(getClass().getResourceAsStream("/bug_516_invalid_expansion.json"), StandardCharsets.UTF_8);
HttpPost post = new HttpPost(ourServerBase+"/ValueSet/%24expand");
post.setEntity(new StringEntity(string, ContentType.parse(ca.uhn.fhir.rest.api.Constants.CT_FHIR_JSON_NEW)));
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
ourLog.info(resp.toString());
assertEquals(400, resp.getStatusLine().getStatusCode());
assertThat(respString, containsString("Unknown FilterOperator code 'n'"));
}finally {
IOUtils.closeQuietly(resp);
}
}
private CodeSystem createExternalCs() {
IFhirResourceDao<CodeSystem> codeSystemDao = myCodeSystemDao;
IResourceTableDao resourceTableDao = myResourceTableDao;
@ -81,40 +59,6 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
return createExternalCs(codeSystemDao, resourceTableDao, termSvc, mySrd);
}
public static CodeSystem createExternalCs(IFhirResourceDao<CodeSystem> theCodeSystemDao, IResourceTableDao theResourceTableDao, IHapiTerminologySvc theTermSvc, ServletRequestDetails theRequestDetails) {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified();
ResourceTable table = theResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
private void createExternalCsAndLocalVs() {
CodeSystem codeSystem = createExternalCs();
@ -204,30 +148,6 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandByIdentifier() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
}
//
@Test
public void testExpandByIdWithFilter() throws IOException {
@ -248,6 +168,49 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
/**
* $expand?identifier=foo is legacy.. It's actually not valid in FHIR as of STU3
* but we supported it for longer than we should have so I don't want to delete
* it right now.
*
* https://groups.google.com/d/msgid/hapi-fhir/CAN2Cfy8kW%2BAOkgC6VjPsU3gRCpExCNZBmJdi-k5R_TWeyWH4tA%40mail.gmail.com?utm_medium=email&utm_source=footer
*/
@Test
public void testExpandByIdentifier() {
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
}
@Test
public void testExpandByUrl() {
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
}
@Test
public void testExpandByValueSet() throws IOException {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
@ -318,7 +281,6 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
}
@Test
public void testExpandInvalidParams() throws IOException {
//@formatter:off
@ -390,10 +352,6 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
assertThat(resp, containsStringIgnoringCase("<code value=\"M\"/>"));
}
@Test
public void testExpandLocalVsAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
@ -461,33 +419,30 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
//@formatter:on
}
/**
* #516
*/
@Test
public void testValiedateCodeAgainstBuiltInSystem() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new StringType("BRN"))
.andParameter("identifier", new StringType("http://hl7.org/fhir/ValueSet/v2-0487"))
.andParameter("system", new StringType("http://hl7.org/fhir/v2/0487"))
.useHttpGet()
.execute();
//@formatter:on
public void testInvalidFilter() throws Exception {
String string = IOUtils.toString(getClass().getResourceAsStream("/bug_516_invalid_expansion.json"), StandardCharsets.UTF_8);
HttpPost post = new HttpPost(ourServerBase + "/ValueSet/%24expand");
post.setEntity(new StringEntity(string, ContentType.parse(ca.uhn.fhir.rest.api.Constants.CT_FHIR_JSON_NEW)));
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).getValue().booleanValue());
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType)respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
ourLog.info(resp.toString());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Burn", ((StringType)respParam.getParameter().get(2).getValue()).getValue());
assertEquals(400, resp.getStatusLine().getStatusCode());
assertThat(respString, containsString("Unknown FilterOperator code 'n'"));
} finally {
IOUtils.closeQuietly(resp);
}
}
@Test
public void testValidateCodeOperationByCodeAndSystemInstance() {
@ -504,7 +459,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
}
@Test
@ -522,7 +477,63 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
}
/**
* Technically this is the wrong param name
*/
@Test
public void testValiedateCodeAgainstBuiltInSystem() {
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new StringType("BRN"))
.andParameter("identifier", new StringType("http://hl7.org/fhir/ValueSet/v2-0487"))
.andParameter("system", new StringType("http://hl7.org/fhir/v2/0487"))
.useHttpGet()
.execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue());
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Burn", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
/**
* Technically this is the right param name
*/
@Test
public void testValiedateCodeAgainstBuiltInSystemByUrl() {
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new StringType("BRN"))
.andParameter("url", new StringType("http://hl7.org/fhir/ValueSet/v2-0487"))
.andParameter("system", new StringType("http://hl7.org/fhir/v2/0487"))
.useHttpGet()
.execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue());
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Burn", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
@AfterClass
@ -530,4 +541,38 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static CodeSystem createExternalCs(IFhirResourceDao<CodeSystem> theCodeSystemDao, IResourceTableDao theResourceTableDao, IHapiTerminologySvc theTermSvc, ServletRequestDetails theRequestDetails) {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified();
ResourceTable table = theResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
}

View File

@ -1,35 +1,38 @@
package ca.uhn.fhir.jpa.provider.r4;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.FilterOperator;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r4.model.ValueSet.FilterOperator;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_CODE_SYSTEM;
import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TerminologyTest.URL_MY_VALUE_SET;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
@ -48,31 +51,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless();
}
/**
* #516
*/
@Test
public void testInvalidFilter() throws Exception {
String string = IOUtils.toString(getClass().getResourceAsStream("/bug_516_invalid_expansion.json"), StandardCharsets.UTF_8);
HttpPost post = new HttpPost(ourServerBase+"/ValueSet/%24expand");
post.setEntity(new StringEntity(string, ContentType.parse(ca.uhn.fhir.rest.api.Constants.CT_FHIR_JSON_NEW)));
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
ourLog.info(resp.toString());
assertEquals(400, resp.getStatusLine().getStatusCode());
assertThat(respString, containsString("Unknown FilterOperator code 'n'"));
}finally {
IOUtils.closeQuietly(resp);
}
}
private CodeSystem createExternalCs() {
IFhirResourceDao<CodeSystem> codeSystemDao = myCodeSystemDao;
IResourceTableDao resourceTableDao = myResourceTableDao;
@ -81,40 +59,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
return createExternalCs(codeSystemDao, resourceTableDao, termSvc, mySrd);
}
public static CodeSystem createExternalCs(IFhirResourceDao<CodeSystem> theCodeSystemDao, IResourceTableDao theResourceTableDao, IHapiTerminologySvc theTermSvc, ServletRequestDetails theRequestDetails) {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified();
ResourceTable table = theResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
private void createExternalCsAndLocalVs() {
CodeSystem codeSystem = createExternalCs();
@ -204,30 +148,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
}
@Test
public void testExpandByIdentifier() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
//@formatter:off
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
//@formatter:on
}
//
@Test
public void testExpandByIdWithFilter() throws IOException {
@ -248,6 +168,24 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
}
@Test
public void testExpandByUrl() {
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "url", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
ourLog.info(resp);
assertThat(resp, stringContainsInOrder(
"<code value=\"11378-7\"/>",
"<display value=\"Systolic blood pressure at First encounter\"/>"));
}
@Test
public void testExpandByValueSet() throws IOException {
ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml");
@ -268,6 +206,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
}
@Test
public void testExpandInlineVsAgainstBuiltInCs() throws IOException {
createLocalVsPointingAtBuiltInCodeSystem();
@ -314,7 +253,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
}
@Test
public void testExpandInvalidParams() throws IOException {
//@formatter:off
@ -327,7 +265,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
.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());
assertEquals("HTTP 400 Bad Request: $expand operation at the type level (no ID specified) requires a url or a valueSet as a part of the request", e.getMessage());
}
//@formatter:on
@ -339,11 +277,11 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.andParameter("url", new UriType("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 instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
}
//@formatter:on
@ -355,11 +293,11 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
.onInstance(myExtensionalVsId)
.named("expand")
.withParameter(Parameters.class, "valueSet", toExpand)
.andParameter("identifier", new UriType("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"))
.andParameter("url", new UriType("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 instance level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
assertEquals("HTTP 400 Bad Request: $expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options.", e.getMessage());
}
//@formatter:on
@ -386,10 +324,6 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
assertThat(resp, containsStringIgnoringCase("<code value=\"M\"/>"));
}
@Test
public void testExpandLocalVsAgainstExternalCs() throws IOException {
createExternalCsAndLocalVs();
@ -424,7 +358,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
.operation()
.onType(ValueSet.class)
.named("expand")
.withParameter(Parameters.class, "identifier", new UriType(URL_MY_VALUE_SET))
.withParameter(Parameters.class, "url", new UriType(URL_MY_VALUE_SET))
.execute();
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
//@formatter:on
@ -457,33 +391,30 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
//@formatter:on
}
/**
* #516
*/
@Test
public void testValiedateCodeAgainstBuiltInSystem() {
//@formatter:off
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new StringType("BRN"))
.andParameter("identifier", new StringType("http://hl7.org/fhir/ValueSet/v2-0487"))
.andParameter("system", new StringType("http://hl7.org/fhir/v2/0487"))
.useHttpGet()
.execute();
//@formatter:on
public void testInvalidFilter() throws Exception {
String string = IOUtils.toString(getClass().getResourceAsStream("/bug_516_invalid_expansion.json"), StandardCharsets.UTF_8);
HttpPost post = new HttpPost(ourServerBase + "/ValueSet/%24expand");
post.setEntity(new StringEntity(string, ContentType.parse(ca.uhn.fhir.rest.api.Constants.CT_FHIR_JSON_NEW)));
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
CloseableHttpResponse resp = ourHttpClient.execute(post);
try {
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).getValue().booleanValue());
String respString = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(respString);
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType)respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
ourLog.info(resp.toString());
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Burn", ((StringType)respParam.getParameter().get(2).getValue()).getValue());
assertEquals(400, resp.getStatusLine().getStatusCode());
assertThat(respString, containsString("Unknown FilterOperator code 'n'"));
} finally {
IOUtils.closeQuietly(resp);
}
}
@Test
public void testValidateCodeOperationByCodeAndSystemInstance() {
@ -500,7 +431,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
}
@Test
@ -518,7 +449,32 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals(true, ((BooleanType)respParam.getParameter().get(0).getValue()).booleanValue());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
}
@Test
public void testValiedateCodeAgainstBuiltInSystem() {
Parameters respParam = ourClient
.operation()
.onType(ValueSet.class)
.named("validate-code")
.withParameter(Parameters.class, "code", new StringType("BRN"))
.andParameter("url", new StringType("http://hl7.org/fhir/ValueSet/v2-0487"))
.andParameter("system", new StringType("http://hl7.org/fhir/v2/0487"))
.useHttpGet()
.execute();
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
ourLog.info(resp);
assertEquals("result", respParam.getParameter().get(0).getName());
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue().booleanValue());
assertEquals("message", respParam.getParameter().get(1).getName());
assertThat(((StringType) respParam.getParameter().get(1).getValue()).getValue(), containsStringIgnoringCase("succeeded"));
assertEquals("display", respParam.getParameter().get(2).getName());
assertEquals("Burn", ((StringType) respParam.getParameter().get(2).getValue()).getValue());
}
@AfterClass
@ -526,4 +482,38 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static CodeSystem createExternalCs(IFhirResourceDao<CodeSystem> theCodeSystemDao, IResourceTableDao theResourceTableDao, IHapiTerminologySvc theTermSvc, ServletRequestDetails theRequestDetails) {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = theCodeSystemDao.create(codeSystem, theRequestDetails).getId().toUnqualified();
ResourceTable table = theResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA").setDisplay("Child AA");
parentA.addChild(childAA, RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA").setDisplay("Child AAA");
childAA.addChild(childAAA, RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB").setDisplay("Child AAB");
childAA.addChild(childAAB, RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB").setDisplay("Child AB");
parentA.addChild(childAB, RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B");
cs.getConcepts().add(parentB);
theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
}

View File

@ -12,6 +12,21 @@
of changes to index tables when updating a resource with contents that
only make minor changes
</action>
<action type="fix">
In FHIR DSTU3 the
<![CDATA[<code>ValueSet/$expand?identifier=foo</code>]]>
and
<![CDATA[<code>ValueSet/$validate-code?identifier=foo</code>]]>
parameters were changed to
<![CDATA[<code>ValueSet/$expand?url=foo</code>]]>
and
<![CDATA[<code>ValueSet/$validate-code?url=foo</code>]]>
respectively, but the JPA server had not caught up. The
JPA DSTU3 server has been adjusted to accept either "identifier"
or "url" (with "url" taking precedence), and the JPA R4 server
has been changed to only accept "url".
Thanks to Avinash Shanbhag for reporting!
</action>
</release>
<release version="3.0.0" date="2017-09-27">
<action type="add">