001/*-
002 * #%L
003 * Smile CDR - CDR
004 * %%
005 * Copyright (C) 2016 - 2024 Smile CDR, Inc.
006 * %%
007 * All rights reserved.
008 * #L%
009 */
010package ca.cdr.api.pub.hl7v2.out;
011
012import ca.cdr.api.util.PublicApiConstants;
013import ca.uhn.fhir.context.FhirContext;
014import jakarta.annotation.Nonnull;
015import jakarta.annotation.Nullable;
016import org.apache.commons.lang3.Validate;
017import org.hl7.fhir.instance.model.api.IBaseReference;
018import org.hl7.fhir.instance.model.api.IBaseResource;
019import org.hl7.fhir.instance.model.api.IIdType;
020
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024
025public class OutboundMappingInstructions {
026
027        public static final OutboundMappingInstructions DEFAULT = new OutboundMappingInstructions.Builder().build();
028        private final Map<IIdType, Long> myUseSourceDataResourceVersions;
029
030        /**
031         * Constructor
032         */
033        private OutboundMappingInstructions(Map<IIdType, Long> theUseSourceDataResourceVersions) {
034                myUseSourceDataResourceVersions = theUseSourceDataResourceVersions;
035        }
036
037        @Nullable
038        public Long getSourceDataResourceVersion(@Nonnull IIdType theId) {
039                IIdType storageId = theId.toUnqualifiedVersionless();
040                return myUseSourceDataResourceVersions.get(storageId);
041        }
042
043        /**
044         * Factory class for {@link OutboundMappingInstructions}
045         */
046        public static class Builder {
047
048                private Map<IIdType, Long> myUseSourceDataResourceVersions;
049
050                /**
051                 * When resolving FHIR resources to use as source data for generating HL7 v2.x messages, this method can be
052                 * used to specify that a specific version of a given resource should be used. This can be used to achieve
053                 * point-in-time architecture with your messages.
054                 *
055                 * @param theId The complete ID, must include the resource type, resource ID, and version.
056                 * @return Returns a reference to the builder for easy method chaining
057                 */
058                public Builder addSourceDataResourceVersion(@Nonnull IIdType theId) {
059                        if (!theId.hasResourceType()) {
060                                throw new IllegalArgumentException("ID does not have a resource type specified");
061                        }
062                        if (!theId.hasIdPart()) {
063                                throw new IllegalArgumentException("ID does not have an ID part specified");
064                        }
065                        if (!theId.isVersionIdPartValidLong()) {
066                                throw new IllegalArgumentException("ID does not have a numeric version specified");
067                        }
068                        IIdType storageId = theId.toUnqualifiedVersionless();
069                        if (myUseSourceDataResourceVersions == null) {
070                                myUseSourceDataResourceVersions = new HashMap<>();
071                        }
072                        myUseSourceDataResourceVersions.put(storageId, theId.getVersionIdPartAsLong());
073                        return this;
074                }
075
076                public OutboundMappingInstructions build() {
077                        if (myUseSourceDataResourceVersions == null) {
078                                myUseSourceDataResourceVersions = Map.of();
079                        }
080                        return new OutboundMappingInstructions(myUseSourceDataResourceVersions);
081                }
082
083                /**
084                 * Examine a MessageHeader resource to extract the list of resources found in
085                 * <code>MessageHeader.focus</code>, and for any versioned references apply
086                 * them to {@link #addSourceDataResourceVersion(IIdType)}.
087                 *
088                 * @param theMessageHeader The MessageHeader to extract from
089                 */
090                public OutboundMappingInstructions.Builder addSourceDataResourceVersionsFromMessageHeaderFocus(
091                                @Nonnull IBaseResource theMessageHeader) {
092
093                        FhirContext ctx = FhirContext.forCached(theMessageHeader.getStructureFhirVersionEnum());
094                        String resourceType = ctx.getResourceType(theMessageHeader);
095                        Validate.isTrue(
096                                        resourceType.equals("MessageHeader"),
097                                        "theMessageHeader must be a MessageHeader resource, found: %s",
098                                        resourceType);
099
100                        List<IBaseReference> focalReferences = ctx.newTerser()
101                                        .getValues(
102                                                        theMessageHeader,
103                                                        PublicApiConstants.FHIRPATH_MESSAGEHEADER_FOCUS,
104                                                        IBaseReference.class,
105                                                        false);
106                        for (IBaseReference next : focalReferences) {
107                                IIdType referenceId = next.getReferenceElement();
108                                if (referenceId.hasResourceType() && referenceId.hasIdPart() && referenceId.hasVersionIdPart()) {
109                                        addSourceDataResourceVersion(referenceId);
110                                }
111                        }
112
113                        return this;
114                }
115        }
116}