All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.uhn.fhir.test.utilities.ITestDataBuilder Maven / Gradle / Ivy

/*-
 * #%L
 * HAPI FHIR Test Utilities
 * %%
 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package ca.uhn.fhir.test.utilities;

import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.MetaUtil;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.InstantType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Date;
import java.util.function.Consumer;

import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.assertj.core.api.Assertions.assertThat;

/**
 * This is an experiment to see if we can make test data creation for storage unit tests a bit more readable.
 */
@SuppressWarnings({"unchecked", "ConstantConditions"})
public interface ITestDataBuilder {
	Logger ourLog = LoggerFactory.getLogger(ITestDataBuilder.class);

	/**
	 * Name chosen to avoid potential for conflict. This is an internal API to this interface.
	 */
	static void __setPrimitiveChild(FhirContext theFhirContext, IBase theTarget, String theElementName, String theElementType, String theValue) {
		BaseRuntimeElementCompositeDefinition def = (BaseRuntimeElementCompositeDefinition) theFhirContext.getElementDefinition(theTarget.getClass());
		BaseRuntimeChildDefinition activeChild = def.getChildByName(theElementName);

		IPrimitiveType booleanType = (IPrimitiveType) activeChild.getChildByName(theElementName).newInstance();
		booleanType.setValueAsString(theValue);
		activeChild.getMutator().addValue(theTarget, booleanType);
	}

	/**
	 * Set Patient.active = true
	 */
	default ICreationArgument withActiveTrue() {
		return t -> __setPrimitiveChild(getFhirContext(), t, "active", "boolean", "true");
	}

	/**
	 * Set Patient.active = false
	 */
	default ICreationArgument withActiveFalse() {
		return t -> __setPrimitiveChild(getFhirContext(), t, "active", "boolean", "false");
	}

	/**
	 * Set Resource.language
	 */
	default ICreationArgument withLanguage(String theLanguage) {
		return t -> __setPrimitiveChild(getFhirContext(), t, "language", "string", theLanguage);
	}

	/**
	 * Set Patient.gender
	 */
	default ICreationArgument withGender(String theGender) {
		return t -> __setPrimitiveChild(getFhirContext(), t, "gender", "code", theGender);
	}

	default ICreationArgument withFamily(String theFamily) {
		return t -> {
			IPrimitiveType family = (IPrimitiveType) getFhirContext().getElementDefinition("string").newInstance();
			family.setValueAsString(theFamily);

			BaseRuntimeElementCompositeDefinition humanNameDef = (BaseRuntimeElementCompositeDefinition) getFhirContext().getElementDefinition("HumanName");
			ICompositeType humanName = (ICompositeType) humanNameDef.newInstance();
			humanNameDef.getChildByName("family").getMutator().addValue(humanName, family);

			BaseRuntimeElementCompositeDefinition resourceDef = (BaseRuntimeElementCompositeDefinition) getFhirContext().getElementDefinition(t.getClass());
			resourceDef.getChildByName("name").getMutator().addValue(t, humanName);
		};
	}

	/**
	 * Patient.name.given
	 */
	default ICreationArgument withGiven(String theName) {
		return withResourcePrimitiveAttribute("name.given", theName);
	}

	/**
	 * Set Patient.birthdate
	 */
	default ICreationArgument withBirthdate(String theBirthdate) {
		return t -> __setPrimitiveChild(getFhirContext(), t, "birthDate", "dateTime", theBirthdate);
	}

	/**
	 * Set Observation.status
	 */
	default ICreationArgument withStatus(String theStatus) {
		return t -> __setPrimitiveChild(getFhirContext(), t, "status", "code", theStatus);
	}

	/**
	 * Set Observation.effectiveDate
	 */
	default ICreationArgument withEffectiveDate(String theDate) {
		return t -> __setPrimitiveChild(getFhirContext(), t, "effectiveDateTime", "dateTime", theDate);
	}

	/**
	 * Set Observation.effectiveDate
	 */
	default ICreationArgument withDateTimeAt(String thePath, String theDate) {
		return t -> __setPrimitiveChild(getFhirContext(), t, thePath, "dateTime", theDate);
	}

	/**
	 * Set [Resource].identifier.system and [Resource].identifier.value
	 */
	default ICreationArgument withIdentifier(String theSystem, String theValue) {
		return t -> {
			IPrimitiveType system = (IPrimitiveType) getFhirContext().getElementDefinition("uri").newInstance();
			system.setValueAsString(theSystem);

			IPrimitiveType value = (IPrimitiveType) getFhirContext().getElementDefinition("string").newInstance();
			value.setValueAsString(theValue);

			BaseRuntimeElementCompositeDefinition identifierDef = (BaseRuntimeElementCompositeDefinition) getFhirContext().getElementDefinition("Identifier");
			ICompositeType identifier = (ICompositeType) identifierDef.newInstance();
			identifierDef.getChildByName("system").getMutator().addValue(identifier, system);
			identifierDef.getChildByName("value").getMutator().addValue(identifier, value);

			RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition((Class) t.getClass());
			resourceDef.getChildByName("identifier").getMutator().addValue(t, identifier);
		};
	}

	/**
	 * Set Organization.name
	 */
	default ICreationArgument withName(String theStatus) {
		return t -> __setPrimitiveChild(getFhirContext(), t, "name", "string", theStatus);
	}

	default ICreationArgument withId(String theId) {
		return t -> {
			assertThat(theId).matches("[a-zA-Z0-9-]+");
			((IBaseResource)t).setId(theId);
		};
	}

	default ICreationArgument withId(IIdType theId) {
		return t -> ((IBaseResource)t).setId(theId.toUnqualifiedVersionless());
	}

	default ICreationArgument withTag(String theSystem, String theCode, Consumer... theModifiers) {
		return t -> {
			IBaseCoding coding = ((IBaseResource) t).getMeta().addTag().setSystem(theSystem).setCode(theCode);
			applyElementModifiers(coding, theModifiers);
		};
	}

	default ICreationArgument withSecurity(String theSystem, String theCode) {
		return t -> ((IBaseResource)t).getMeta().addSecurity().setSystem(theSystem).setCode(theCode);
	}

	default ICreationArgument withProfile(String theProfile) {
		return t -> ((IBaseResource)t).getMeta().addProfile(theProfile);
	}

	default ICreationArgument withSource(FhirContext theContext, String theSource) {
		return t -> MetaUtil.setSource(theContext, ((IBaseResource)t).getMeta(), theSource);
	}

	default ICreationArgument withSource(String theSource) {
		return t -> MetaUtil.setSource(getFhirContext(), ((IBaseResource)t).getMeta(), theSource);
	}

	default ICreationArgument withLastUpdated(Date theLastUpdated) {
		return t -> ((IBaseResource)t).getMeta().setLastUpdated(theLastUpdated);
	}

	default ICreationArgument withLastUpdated(String theIsoDate) {
		return t -> ((IBaseResource)t).getMeta().setLastUpdated(new InstantType(theIsoDate).getValue());
	}

	default IIdType createEncounter(ICreationArgument... theModifiers) {
		return createResource("Encounter", theModifiers);
	}

	default IIdType createGroup(ICreationArgument... theModifiers) {
		return createResource("Group", theModifiers);
	}

	default IIdType createObservation(ICreationArgument... theModifiers) {
		return createResource("Observation", theModifiers);
	}

	default IIdType createObservation(Collection theModifiers) {
		return createResource("Observation", theModifiers.toArray(new ICreationArgument[0]));
	}

	default IBaseResource buildPatient(ICreationArgument... theModifiers) {
		return buildResource("Patient", theModifiers);
	}

	default IIdType createPatient(ICreationArgument... theModifiers) {
		return createResource("Patient", theModifiers);
	}

	default IIdType createOrganization(ICreationArgument... theModifiers) {
		return createResource("Organization", theModifiers);
	}

	default IIdType createPractitioner(ICreationArgument... theModifiers) {
		return createResource("Practitioner", theModifiers);
	}

	default IIdType createResource(String theResourceType, ICreationArgument... theModifiers) {
		IBaseResource resource = buildResource(theResourceType, theModifiers);

		if (ourLog.isDebugEnabled()) {
			ourLog.debug("Creating {}", getFhirContext().newJsonParser().encodeResourceToString(resource));
		}

		if (isNotBlank(resource.getIdElement().getValue())) {
			return doUpdateResource(resource);
		} else {
			return doCreateResource(resource);
		}
	}

	default IIdType createResourceFromJson(String theJson, ICreationArgument... theModifiers) {
		IBaseResource resource = getFhirContext().newJsonParser().parseResource(theJson);
		applyElementModifiers(resource, theModifiers);

		if (ourLog.isDebugEnabled()) {
			ourLog.debug("Creating {}", getFhirContext().newJsonParser().encodeResourceToString(resource));
		}

		if (isNotBlank(resource.getIdElement().getValue())) {
			return doUpdateResource(resource);
		} else {
			return doCreateResource(resource);
		}
	}

	default  T buildResource(String theResourceType, ICreationArgument... theModifiers) {
		IBaseResource resource = getFhirContext().getResourceDefinition(theResourceType).newInstance();
		applyElementModifiers(resource, theModifiers);
		return (T) resource;
	}

	default ICreationArgument withSubject(@Nullable IIdType theSubject) {
		return withReference("subject", theSubject);
	}

	default ICreationArgument withSubject(@Nullable String theSubject) {
		return withSubject(new IdType(theSubject));
	}

	default ICreationArgument withPatient(@Nullable IIdType theSubject) {
		return withReference("patient", theSubject);
	}

	default ICreationArgument withPatient(@Nullable String theSubject) {
		return withSubject(new IdType(theSubject));
	}

	default ICreationArgument withGroupMember(@Nullable IIdType theMember) {
		return withResourcePrimitiveAttribute("member.entity.reference", theMember);
	}

	default ICreationArgument withGroupMember(@Nullable String theMember) {
		return withGroupMember(new IdType(theMember));
	}

	default ICreationArgument withEncounter(@Nullable String theEncounter) {
		return withReference("encounter", new IdType(theEncounter));
	}

	@Nonnull
	default ICreationArgument withReference(String theReferenceName, @Nullable IIdType theReferenceValue) {
		return t -> {
			if (theReferenceValue != null && theReferenceValue.getValue() != null) {
				IBaseReference reference = (IBaseReference) getFhirContext().getElementDefinition("Reference").newInstance();
				reference.setReference(theReferenceValue.getValue());

				RuntimeResourceDefinition resourceDef = getFhirContext().getResourceDefinition((IBaseResource) t);
				resourceDef.getChildByName(theReferenceName).getMutator().addValue(t, reference);
			}
		};
	}

	default Consumer withPrimitiveAttribute(String thePath, Object theValue) {
		return t -> {
			FhirTerser terser = getFhirContext().newTerser();
			terser.addElement(t, thePath, "" + theValue);
		};
	}

	default ICreationArgument withResourcePrimitiveAttribute(String thePath, Object theValue) {
		return t -> {
			FhirTerser terser = getFhirContext().newTerser();
			terser.addElement(t, thePath, "" + theValue);
		};
	}

	default  ICreationArgument withElementAt(String thePath, Consumer... theModifiers) {
		return t -> {
			FhirTerser terser = getFhirContext().newTerser();
			E element = terser.addElement(t, thePath);
			applyElementModifiers(element, theModifiers);
		};
	}

	default ICreationArgument withQuantityAtPath(String thePath, Number theValue, String theSystem, String theCode) {
		return withElementAt(thePath,
			withPrimitiveAttribute("value", theValue),
			withPrimitiveAttribute("system", theSystem),
			withPrimitiveAttribute("code", theCode)
		);
	}

	/**
	 * Create an Element and apply modifiers
	 *
	 * @param theElementType the FHIR Element type to create
	 * @param theModifiers   modifiers to apply after construction
	 * @return the Element
	 */
	default IBase withElementOfType(String theElementType, Consumer... theModifiers) {
		IBase element = getFhirContext().getElementDefinition(theElementType).newInstance();
		applyElementModifiers(element, theModifiers);
		return element;
	}

	default  void applyElementModifiers(E element, Consumer[] theModifiers) {
		for (Consumer nextModifier : theModifiers) {
			nextModifier.accept(element);
		}
	}

	default ICreationArgument withObservationCode(@Nullable String theSystem, @Nullable String theCode) {
		return withObservationCode(theSystem, theCode, null);
	}

	default ICreationArgument withObservationCode(@Nullable String theSystem, @Nullable String theCode, @Nullable String theDisplay) {
		return withCodingAt("code.coding", theSystem, theCode, theDisplay);
	}

	default  ICreationArgument withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue) {
		return withCodingAt(thePath, theSystem, theValue, null);
	}

	default  ICreationArgument withCodingAt(String thePath, @Nullable String theSystem, @Nullable String theValue, @Nullable String theDisplay) {
		return withElementAt(thePath,
			withPrimitiveAttribute("system", theSystem),
			withPrimitiveAttribute("code", theValue),
			withPrimitiveAttribute("display", theDisplay)
		);
	}

	default ICreationArgument withObservationComponent(ICreationArgument... theModifiers) {
		return withElementAt("component", theModifiers);
	}

	default ICreationArgument withObservationHasMember(@Nullable IIdType theHasMember) {
		return withReference("hasMember", theHasMember);
	}

	default ICreationArgument withOrganization(@Nullable IIdType theHasMember) {
		return withReference("managingOrganization", theHasMember);
	}

	/**
	 * Users of this API must implement this method
	 */
	IIdType doCreateResource(IBaseResource theResource);

	/**
	 * Users of this API must implement this method
	 */
	IIdType doUpdateResource(IBaseResource theResource);

	/**
	 * Users of this API must implement this method
	 */
	FhirContext getFhirContext();

	default ICreationArgument[] asArray(ICreationArgument theIBaseResourceConsumer) {
		return new ICreationArgument[]{theIBaseResourceConsumer};
	}

	interface Support {
		void setRequestId(String theRequestId);

		FhirContext getFhirContext();

		IIdType doCreateResource(IBaseResource theResource);

		IIdType doUpdateResource(IBaseResource theResource);
	}

	interface WithSupport extends ITestDataBuilder {
		Support getTestDataBuilderSupport();

		@Override
		default FhirContext getFhirContext() {
			return getTestDataBuilderSupport().getFhirContext();

		}

		@Override
		default IIdType doCreateResource(IBaseResource theResource) {
			return getTestDataBuilderSupport().doCreateResource(theResource);
		}

		@Override
		default IIdType doUpdateResource(IBaseResource theResource) {
			return getTestDataBuilderSupport().doUpdateResource(theResource);
		}
	}

	interface ICreationArgument extends Consumer {
		// nothing
	}

	/**
	 * Dummy support to use ITestDataBuilder as just a builder, not a DAO
	 */
	class SupportNoDao implements Support {
		final FhirContext myFhirContext;

		public SupportNoDao(FhirContext theFhirContext) {
			myFhirContext = theFhirContext;
		}

		@Override
		public void setRequestId(String theRequestId) {
			// do nothing
		}

		@Override
		public FhirContext getFhirContext() {
			return myFhirContext;
		}

		@Override
		public IIdType doCreateResource(IBaseResource theResource) {
			Validate.isTrue(false, "Create not supported");
			return null;
		}

		@Override
		public IIdType doUpdateResource(IBaseResource theResource) {
			Validate.isTrue(false, "Update not supported");
			return null;
		}
	}


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy