package ca.uhn.fhir.parser; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import java.util.ArrayList; import java.util.List; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBase; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.api.annotation.Extension; import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.parser.CustomResource364Dstu3.CustomResource364CustomDate; import ca.uhn.fhir.rest.server.AddProfileTagEnum; import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.TestUtil; public class CustomTypeDstu3Test { private static FhirContext ourCtx = FhirContext.forDstu3(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeDstu3Test.class); @Before public void before() { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); } @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } @SuppressWarnings("serial") @ResourceDef public static class PatientWithExtensionWithTwoTypes extends Patient { @Child(name = "foo", type = { DateTimeType.class, Period.class }, min = 0, max = 1) @Extension(url = "http://example.com/extension#foo", definedLocally = true, isModifier = false) @Description(shortDefinition = "Some description") private Type foo; public Type getFoo() { return foo; } public void setFoo(Type theFoo) { foo = theFoo; } } @Test public void testExtensionWithTwoTypes() { PatientWithExtensionWithTwoTypes pt = new PatientWithExtensionWithTwoTypes(); pt.setFoo(new DateTimeType("2011-01-01T00:00:00Z")); String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pt); ourLog.info(encoded); pt = ourCtx.newXmlParser().parseResource(PatientWithExtensionWithTwoTypes.class, encoded); assertEquals("2011-01-01T00:00:00Z", ((DateTimeType)pt.getFoo()).getValueAsString()); } @SuppressWarnings("serial") @ResourceDef public static class PatientWithExtensionWithOneTypes extends Patient { @Child(name = "foo", type = { DateTimeType.class }, min = 0, max = 1) @Extension(url = "http://example.com/extension#foo", definedLocally = true, isModifier = false) @Description(shortDefinition = "Some description") private Type foo; public Type getFoo() { return foo; } public void setFoo(Type theFoo) { foo = theFoo; } } @Test public void testExtensionWithOneTypes() { PatientWithExtensionWithOneTypes pt = new PatientWithExtensionWithOneTypes(); pt.setFoo(new DateTimeType("2011-01-01T00:00:00Z")); String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pt); ourLog.info(encoded); pt = ourCtx.newXmlParser().parseResource(PatientWithExtensionWithOneTypes.class, encoded); assertEquals("2011-01-01T00:00:00Z", ((DateTimeType)pt.getFoo()).getValueAsString()); } /** * See #364 */ @Test public void testCustomTypeWithCustomDatatype() { FhirContext context = FhirContext.forDstu3(); context.registerCustomType(CustomResource364Dstu3.class); context.registerCustomType(CustomResource364CustomDate.class); IParser parser = context.newXmlParser(); CustomResource364Dstu3 resource = new CustomResource364Dstu3(); resource.setBaseValues(new CustomResource364CustomDate().setDate(new DateTimeType("2016-05-13"))); String xml = parser.encodeResourceToString(resource); ourLog.info(xml); //@formatter:on assertThat(xml, stringContainsInOrder( "<CustomResource xmlns=\"http://hl7.org/fhir\">", "<meta><profile value=\"http://hl7.org/fhir/profiles/custom-resource\"/></meta>", "<baseValueCustomDate><date value=\"2016-05-13\"/></baseValueCustomDate>", "</CustomResource>")); //@formatter:on CustomResource364Dstu3 parsedResource = parser.parseResource(CustomResource364Dstu3.class, xml); assertEquals("2016-05-13", ((CustomResource364CustomDate) parsedResource.getBaseValues()).getDate().getValueAsString()); } /** * See #364 */ @Test public void testCustomTypeWithPrimitiveType() { FhirContext context = FhirContext.forDstu3(); context.registerCustomTypes(new ArrayList<Class<? extends IBase>>()); IParser parser = context.newXmlParser(); CustomResource364Dstu3 resource = new CustomResource364Dstu3(); resource.setBaseValues(new StringType("2016-05-13")); String xml = parser.encodeResourceToString(resource); //@formatter:on assertThat(xml, stringContainsInOrder( "<CustomResource xmlns=\"http://hl7.org/fhir\">", "<meta><profile value=\"http://hl7.org/fhir/profiles/custom-resource\"/></meta>", "<baseValueString value=\"2016-05-13\"/>", "</CustomResource>")); //@formatter:on CustomResource364Dstu3 parsedResource = parser.parseResource(CustomResource364Dstu3.class, xml); assertEquals("2016-05-13", ((StringType) parsedResource.getBaseValues()).getValueAsString()); } @Test public void parseBundleWithResourceDirective() { String input = createBundle(createResource(false), createResource(true)); FhirContext ctx = FhirContext.forDstu3(); ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class); Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, input); Patient res0 = (Patient) bundle.getEntry().get(0).getResource(); assertEquals(0, res0.getMeta().getProfile().size()); List<org.hl7.fhir.dstu3.model.Extension> exts = res0.getExtensionsByUrl("http://example.com/Weight"); assertEquals(1, exts.size()); assertEquals("185 cm", ((StringType) exts.get(0).getValue()).getValue()); MyCustomPatient res1 = (MyCustomPatient) bundle.getEntry().get(1).getResource(); assertEquals(1, res1.getMeta().getProfile().size()); assertEquals("http://example.com/foo", res1.getMeta().getProfile().get(0).getValue()); exts = res1.getExtensionsByUrl("http://example.com/Weight"); assertEquals(0, exts.size()); assertEquals("185 cm", res1.getWeight().getValue()); } @Test public void parseResourceWithDirective() { String input = createResource(true); FhirContext ctx = FhirContext.forDstu3(); ctx.setDefaultTypeForProfile("http://example.com/foo", MyCustomPatient.class); MyCustomPatient parsed = (MyCustomPatient) ctx.newXmlParser().parseResource(input); assertEquals(1, parsed.getMeta().getProfile().size()); assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue()); List<org.hl7.fhir.dstu3.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight"); assertEquals(0, exts.size()); assertEquals("185 cm", parsed.getWeight().getValue()); } @Test public void parseResourceWithNoDirective() { String input = createResource(true); FhirContext ctx = FhirContext.forDstu3(); Patient parsed = (Patient) ctx.newXmlParser().parseResource(input); assertEquals(1, parsed.getMeta().getProfile().size()); assertEquals("http://example.com/foo", parsed.getMeta().getProfile().get(0).getValue()); List<org.hl7.fhir.dstu3.model.Extension> exts = parsed.getExtensionsByUrl("http://example.com/Weight"); assertEquals(1, exts.size()); assertEquals("185 cm", ((StringType) exts.get(0).getValue()).getValue()); } @Test public void testAccessEmptyMetaLists() { Patient p = new Patient(); assertThat(p.getMeta().getProfile(), empty()); assertThat(p.getMeta().getFormatCommentsPost(), empty()); assertThat(p.getMeta().getFormatCommentsPre(), empty()); assertThat(p.getMeta().getLastUpdated(), nullValue()); assertThat(p.getMeta().getSecurity(), empty()); assertThat(p.getMeta().getSecurity("foo", "bar"), nullValue()); assertThat(p.getMeta().getTag(), empty()); assertThat(p.getMeta().getTag("foo", "bar"), nullValue()); assertThat(p.getMeta().getVersionId(), nullValue()); } @Test public void testEncodeCompleteMetaLists() { Patient p = new Patient(); p.getMeta().addProfile("http://foo/profile1"); p.getMeta().addProfile("http://foo/profile2"); p.getMeta().addSecurity().setSystem("SEC_S1").setCode("SEC_C1").setDisplay("SED_D1"); p.getMeta().addSecurity().setSystem("SEC_S2").setCode("SEC_C2").setDisplay("SED_D2"); p.getMeta().addTag().setSystem("TAG_S1").setCode("TAG_C1").setDisplay("TAG_D1"); p.getMeta().addTag().setSystem("TAG_S2").setCode("TAG_C2").setDisplay("TAG_D2"); String out = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p); ourLog.info(out); //@formatter:off assertThat(out, stringContainsInOrder( "<meta>", "<profile value=\"http://foo/profile1\"/>", "<profile value=\"http://foo/profile2\"/>", "<security>", "<system value=\"SEC_S1\"/>", "<code value=\"SEC_C1\"/>", "<display value=\"SED_D1\"/>", "</security>", "<security>", "<system value=\"SEC_S2\"/>", "<code value=\"SEC_C2\"/>", "<display value=\"SED_D2\"/>", "</security>", "<tag>", "<system value=\"TAG_S1\"/>", "<display value=\"TAG_D1\"/>", "</tag>", "<tag>", "<system value=\"TAG_S2\"/>", "<display value=\"TAG_D2\"/>", "</tag>", "</meta>")); //@formatter:on } @Test public void testEncodeWithCustomType() { MyCustomPatient patient = new MyCustomPatient(); patient.addIdentifier().setSystem("urn:system").setValue("1234"); patient.addName().setFamily("Rossi").addGiven("Mario"); patient.setInsulinLevel(new Quantity()); patient.setGlucoseLevel(new Quantity()); patient.setHbA1c(new Quantity()); patient.setBloodPressure(new Quantity()); patient.setCholesterol(new Quantity()); patient.setWeight(new StringDt("80 kg")); patient.setWeight(new StringDt("185 cm")); patient.setCheckDates(new ArrayList<DateTimeDt>()); patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11")); ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); IParser p = ourCtx.newXmlParser().setPrettyPrint(true); String messageString = p.encodeResourceToString(patient); ourLog.info(messageString); //@formatter:off assertThat(messageString, stringContainsInOrder( "<meta>", "<profile value=\"http://example.com/foo\"/>", "</meta>")); //@formatter:on //@formatter:off assertThat(messageString, not(stringContainsInOrder( "<meta>", "<profile value=\"http://example.com/foo\"", "/>", "<profile value=\"http://example.com/foo\"/>", "</meta>"))); //@formatter:on } @Test public void testEncodeWithCustomTypeAndAutoInsertedProfile() { MyCustomPatient patient = new MyCustomPatient(); patient.getMeta().addProfile("http://example.com/foo"); patient.getMeta().addProfile("http://example.com/bar"); patient.addIdentifier().setSystem("urn:system").setValue("1234"); patient.addName().setFamily("Rossi").addGiven("Mario"); patient.setInsulinLevel(new Quantity()); patient.setGlucoseLevel(new Quantity()); patient.setHbA1c(new Quantity()); patient.setBloodPressure(new Quantity()); patient.setCholesterol(new Quantity()); patient.setWeight(new StringDt("80 kg")); patient.setWeight(new StringDt("185 cm")); patient.setCheckDates(new ArrayList<DateTimeDt>()); patient.getCheckDates().add(new DateTimeDt("2014-01-26T11:11:11")); ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); IParser p = ourCtx.newXmlParser().setPrettyPrint(true); String messageString = p.encodeResourceToString(patient); ourLog.info(messageString); //@formatter:off assertThat(messageString, stringContainsInOrder( "<meta>", "<profile value=\"http://example.com/foo\"/>", "<profile value=\"http://example.com/bar\"/>", "</meta>")); //@formatter:on //@formatter:off assertThat(messageString, not(stringContainsInOrder( "<meta>", "<profile value=\"http://example.com/foo\"", "/>", "<profile value=\"http://example.com/foo\"/>", "</meta>"))); //@formatter:on } /** * See #318 */ @Test public void testParseResourceWithContainedResourcesWithProfile() { //@formatter:off String input = "<MedicationRequest xmlns=\"http://hl7.org/fhir\">" + "<id value=\"44cfa24c-52e1-a8ff-8428-4e7ce1165460-local\"/> " + "<meta> " + "<profile value=\"http://fhir.something.com/StructureDefinition/our-medication-order\"/> " + "</meta> " + "<contained> " + "<Medication xmlns=\"http://hl7.org/fhir\"> " + "<id value=\"1\"/>" + "<meta> " + "<profile value=\"http://fhir.something.com/StructureDefinition/our-medication\"/> " + "</meta> " + "<code> " + "<text value=\"medication\"/> " + "</code> " + "</Medication> " + "</contained> " + "<medication> " + "<reference value=\"#1\"/> " + "</medication> " + "</MedicationRequest>"; //@formatter:on FhirContext ctx = FhirContext.forDstu3(); ctx.setDefaultTypeForProfile("http://fhir.something.com/StructureDefinition/our-medication", MyMedication.class); MedicationRequest mo = ctx.newXmlParser().parseResource(MedicationRequest.class, input); assertEquals(MyMedication.class, mo.getContained().get(0).getClass()); } public static String createBundle(String... theResources) { StringBuilder b = new StringBuilder(); b.append("<Bundle xmlns=\"http://hl7.org/fhir\">\n"); for (String next : theResources) { b.append(" <entry>\n"); b.append(" <resource>\n"); b.append(next); b.append(" </resource>\n"); b.append(" </entry>\n"); } b.append("</Bundle>"); return b.toString(); } public static String createResource(boolean theWithProfile) { StringBuilder b = new StringBuilder(); b.append("<Patient xmlns=\"http://hl7.org/fhir\">\n"); if (theWithProfile) { b.append(" <meta>\n"); b.append(" <profile value=\"http://example.com/foo\"/>\n"); b.append(" </meta>\n"); } b.append(" <extension url=\"http://example.com/BloodPressure\">\n"); b.append(" <valueQuantity>\n"); b.append(" <value value=\"110\"/>\n"); b.append(" <system value=\"http://unitsofmeasure.org\"/>\n"); b.append(" <code value=\"mmHg\"/>\n"); b.append(" </valueQuantity>\n"); b.append(" </extension>\n"); b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n"); b.append(" <valueDateTime value=\"2010-01-02\"/>\n"); b.append(" </modifierExtension>\n"); b.append(" <modifierExtension url=\"http://example.com/diabetes2\">\n"); b.append(" <valueDateTime value=\"2014-01-26T11:11:11\"/>\n"); b.append(" </modifierExtension>\n"); b.append(" <extension url=\"http://example.com/Cholesterol\">\n"); b.append(" <valueQuantity>\n"); b.append(" <value value=\"2\"/>\n"); b.append(" <system value=\"http://unitsofmeasure.org\"/>\n"); b.append(" <code value=\"mmol/l\"/>\n"); b.append(" </valueQuantity>\n"); b.append(" </extension>\n"); b.append(" <extension url=\"http://example.com/Glucose\">\n"); b.append(" <valueQuantity>\n"); b.append(" <value value=\"95\"/>\n"); b.append(" <system value=\"http://unitsofmeasure.org\"/>\n"); b.append(" <code value=\"mg/dl\"/>\n"); b.append(" </valueQuantity>\n"); b.append(" </extension>\n"); b.append(" <extension url=\"http://example.com/HbA1c\">\n"); b.append(" <valueQuantity>\n"); b.append(" <value value=\"48\"/>\n"); b.append(" <system value=\"http://unitsofmeasure.org\"/>\n"); b.append(" <code value=\"mmol/mol\"/>\n"); b.append(" </valueQuantity>\n"); b.append(" </extension>\n"); b.append(" <extension url=\"http://example.com/Insuline\">\n"); b.append(" <valueQuantity>\n"); b.append(" <value value=\"125\"/>\n"); b.append(" <system value=\"http://unitsofmeasure.org\"/>\n"); b.append(" <code value=\"pmol/l\"/>\n"); b.append(" </valueQuantity>\n"); b.append(" </extension>\n"); b.append(" <extension url=\"http://example.com/Weight\">\n"); b.append(" <valueString value=\"185 cm\"/>\n"); b.append(" </extension>\n"); b.append(" <identifier>\n"); b.append(" <system value=\"urn:system\"/>\n"); b.append(" <value value=\"1234\"/>\n"); b.append(" </identifier>\n"); b.append(" <name>\n"); b.append(" <family value=\"Rossi\"/>\n"); b.append(" <given value=\"Mario\"/>\n"); b.append(" </name>\n"); b.append("</Patient>"); String input = b.toString(); return input; } @ResourceDef(name = "Patient", profile = "http://example.com/foo") public static class MyCustomPatient extends Patient { private static final long serialVersionUID = 1L; @Child(name = "bloodPressure") // once every 3 month. The average target is 130/80 mmHg or less @Extension(url = "http://example.com/BloodPressure", definedLocally = false, isModifier = false) @Description(shortDefinition = "The value of the patient's blood pressure") private Quantity myBloodPressure; // Dates of periodic tests @Child(name = "CheckDates", max = Child.MAX_UNLIMITED) @Extension(url = "http://example.com/diabetes2", definedLocally = false, isModifier = true) @Description(shortDefinition = "Dates of periodic tests") private List<DateTimeDt> myCheckDates; @Child(name = "cholesterol") // once a year. The target is triglycerides =< 2 mmol/l e cholesterol =< 4 mmol/l @Extension(url = "http://example.com/Cholesterol", definedLocally = false, isModifier = false) @Description(shortDefinition = "The value of the patient's cholesterol") private Quantity myCholesterol; @Child(name = "glucoseLevel") // fingerprick test @Extension(url = "http://example.com/Glucose", definedLocally = false, isModifier = false) @Description(shortDefinition = "The value of the patient's blood glucose") private Quantity myGlucoseLevel; // Periodic Tests @Child(name = "hbA1c") // once every 6 month. The average target is 53 mmol/mol (or 7%) or less. @Extension(url = "http://example.com/HbA1c", definedLocally = false, isModifier = false) @Description(shortDefinition = "The value of the patient's glucose") private Quantity myHbA1c; @Child(name = "Height") @Extension(url = "http://example.com/Height", definedLocally = false, isModifier = false) @Description(shortDefinition = "The patient's height in cm") private StringDt myHeight; @Child(name = "insulinLevel") // Normal range is [43,208] pmol/l @Extension(url = "http://example.com/Insuline", definedLocally = false, isModifier = false) @Description(shortDefinition = "The value of the patient's insulin") private Quantity myInsulinLevel; // Other parameters @Child(name = "weight") @Extension(url = "http://example.com/Weight", definedLocally = false, isModifier = false) @Description(shortDefinition = "The patient's weight in Kg") private StringDt myWeight; public Quantity Cholesterol() { if (myCholesterol == null) { myCholesterol = new Quantity(); } myCholesterol.getValue(); myCholesterol.getSystem(); myCholesterol.getCode(); return myCholesterol; } public Quantity getBloodPressure() { if (myBloodPressure == null) { myBloodPressure = new Quantity(); } myBloodPressure.getValue(); myBloodPressure.getSystem(); myBloodPressure.getCode(); return myBloodPressure; } public List<DateTimeDt> getCheckDates() { if (myCheckDates == null) { myCheckDates = new ArrayList<DateTimeDt>(); } return myCheckDates; } public Quantity getGlucoseLevel() { if (myGlucoseLevel == null) { myGlucoseLevel = new Quantity(); } myGlucoseLevel.getValue(); myGlucoseLevel.getSystem(); myGlucoseLevel.getCode(); return myGlucoseLevel; } public Quantity getHbA1c() { if (myHbA1c == null) { myHbA1c = new Quantity(); } myHbA1c.getValue(); myHbA1c.getSystem(); myHbA1c.getCode(); return myHbA1c; } public StringDt getHeight() { if (myHeight == null) { myHeight = new StringDt(); } return myHeight; } public Quantity getInsulinLevel() { if (myInsulinLevel == null) { myInsulinLevel = new Quantity(); } myInsulinLevel.getValue(); myInsulinLevel.getSystem(); myInsulinLevel.getCode(); return myInsulinLevel; } public StringDt getWeight() { if (myWeight == null) { myWeight = new StringDt(); } return myWeight; } @Override public boolean isEmpty() { return super.isEmpty() && ElementUtil.isEmpty(myInsulinLevel, myGlucoseLevel, myHbA1c, myBloodPressure, myCholesterol, myWeight, myHeight, myCheckDates); } public void setBloodPressure(Quantity bloodPressure) { myBloodPressure = bloodPressure; myBloodPressure.setValue(110); myBloodPressure.setSystem("http://unitsofmeasure.org"); myBloodPressure.setCode("mmHg"); } public void setCheckDates(List<DateTimeDt> theCheckDates) { myCheckDates = theCheckDates; myCheckDates.add(new DateTimeDt("2010-01-02")); } public void setCholesterol(Quantity cholesterol) { myCholesterol = cholesterol; myCholesterol.setValue(2); myCholesterol.setSystem("http://unitsofmeasure.org"); myCholesterol.setCode("mmol/l"); } public void setGlucoseLevel(Quantity glucoseLevel) { myGlucoseLevel = glucoseLevel; myGlucoseLevel.setValue(95); myGlucoseLevel.setSystem("http://unitsofmeasure.org"); myGlucoseLevel.setCode("mg/dl"); } public void setHbA1c(Quantity hba1c) { myHbA1c = hba1c; myHbA1c.setValue(48); myHbA1c.setSystem("http://unitsofmeasure.org"); myHbA1c.setCode("mmol/mol"); } public void setHeight(StringDt height) { myHeight = height; } // Setter/Getter methods public void setInsulinLevel(Quantity insulinLevel) { myInsulinLevel = insulinLevel; myInsulinLevel.setValue(125); myInsulinLevel.setSystem("http://unitsofmeasure.org"); myInsulinLevel.setCode("pmol/l"); } public void setWeight(StringDt weight) { myWeight = weight; } } @ResourceDef() public static class MyMedication extends Medication { private static final long serialVersionUID = 1L; } }