001/*-
002 * #%L
003 * HAPI FHIR - Master Data Management
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.mdm.util;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.mdm.api.IMdmSettings;
024import ca.uhn.fhir.mdm.api.MdmConstants;
025import ca.uhn.fhir.mdm.model.CanonicalEID;
026import org.hl7.fhir.instance.model.api.IAnyResource;
027import org.hl7.fhir.instance.model.api.IBaseResource;
028import org.springframework.beans.factory.annotation.Autowired;
029import org.springframework.stereotype.Service;
030
031import java.util.Collections;
032import java.util.List;
033import java.util.UUID;
034import java.util.stream.Collectors;
035
036@Service
037public class EIDHelper {
038
039        private final FhirContext myFhirContext;
040        private final IMdmSettings myMdmSettings;
041
042        @Autowired
043        public EIDHelper(FhirContext theFhirContext, IMdmSettings theMdmSettings) {
044                myFhirContext = theFhirContext;
045                myMdmSettings = theMdmSettings;
046        }
047
048        public CanonicalEID createHapiEid() {
049                return new CanonicalEID(
050                                MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM,
051                                UUID.randomUUID().toString(),
052                                null);
053        }
054
055        /**
056         * Given an {@link IAnyResource} representing a type supported by MDM, retrieve their externally-assigned EID,
057         * represented as a {@link CanonicalEID}
058         *
059         * @param theResource the resource to extract the EID from.
060         *
061         * @return An optional {@link CanonicalEID} representing the external EID. Absent if the EID is not present.
062         */
063        public List<CanonicalEID> getExternalEid(IBaseResource theResource) {
064                String resourceType = myFhirContext.getResourceType(theResource);
065                return CanonicalEID.extractFromResource(
066                                myFhirContext,
067                                myMdmSettings.getMdmRules().getEnterpriseEIDSystemForResourceType(resourceType),
068                                theResource);
069        }
070
071        /**
072         * Given an {@link IAnyResource} representing a type supported by MDM, retrieve their internally-assigned EID,
073         * represented as a {@link CanonicalEID}
074         *
075         * @param theResource the resource to extract the EID from.
076         *
077         * @return An optional {@link CanonicalEID} representing the internal EID. Absent if the EID is not present.
078         */
079        public List<CanonicalEID> getHapiEid(IAnyResource theResource) {
080                return CanonicalEID.extractFromResource(
081                                myFhirContext, MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM, theResource);
082        }
083
084        /**
085         * Determines whether two lists of {@link CanonicalEID} have any intersection. Two resources are considered a match if
086         * a single {@link CanonicalEID} matches between the two collections.
087         *
088         * @param theFirstResourceEids the first EID
089         * @param theSecondResourceEids the second EID
090         *
091         * @return a boolean indicating whether there is a match between these two identifier sets.
092         */
093        public boolean eidMatchExists(List<CanonicalEID> theFirstResourceEids, List<CanonicalEID> theSecondResourceEids) {
094                List<String> collect =
095                                theFirstResourceEids.stream().map(CanonicalEID::getValue).collect(Collectors.toList());
096                List<String> collect1 =
097                                theSecondResourceEids.stream().map(CanonicalEID::getValue).collect(Collectors.toList());
098                return !Collections.disjoint(collect, collect1);
099        }
100
101        /**
102         * An incoming resource is a potential duplicate if it matches a source resource that has a golden resource with an
103         * official EID, but the incoming resource also has an EID that does not match.
104         */
105        public boolean hasEidOverlap(IAnyResource theExistingGoldenResource, IAnyResource theComparingGoldenResource) {
106                List<CanonicalEID> firstEids = this.getExternalEid(theExistingGoldenResource);
107                List<CanonicalEID> secondEids = this.getExternalEid(theComparingGoldenResource);
108                if (firstEids.isEmpty() || secondEids.isEmpty()) {
109                        return false;
110                }
111                return this.eidMatchExists(firstEids, secondEids);
112        }
113}