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.mdm.api.MdmConstants;
023import jakarta.annotation.Nonnull;
024import org.hl7.fhir.instance.model.api.IBaseCoding;
025import org.hl7.fhir.instance.model.api.IBaseResource;
026
027import java.util.Optional;
028
029public final class MdmResourceUtil {
030
031        private MdmResourceUtil() {}
032
033        /**
034         * If the resource is tagged as not managed by MDM, return false. Otherwise true.
035         *
036         * @param theBaseResource The FHIR resource that is potentially managed by MDM.
037         * @return A boolean indicating whether MDM can manage this resource.
038         */
039        public static boolean isMdmAllowed(IBaseResource theBaseResource) {
040                return theBaseResource.getMeta().getTag(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_NO_MDM_MANAGED)
041                                == null;
042        }
043
044        /**
045         * Checks for the presence of the MDM-managed tag, indicating the MDM system has ownership
046         * of this golden resource's links.
047         *
048         * @param theBaseResource the resource to check.
049         * @return a boolean indicating whether or not MDM manages this FHIR resource.
050         */
051        public static boolean isMdmManaged(IBaseResource theBaseResource) {
052                return resourceHasTag(theBaseResource, MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_HAPI_MDM_MANAGED);
053        }
054
055        public static boolean isGoldenRecord(IBaseResource theBaseResource) {
056                return resourceHasTag(
057                                theBaseResource, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD);
058        }
059
060        public static boolean hasGoldenRecordSystemTag(IBaseResource theIBaseResource) {
061                return resourceHasTagWithSystem(theIBaseResource, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS);
062        }
063
064        public static boolean containsTagWithSystem(IBaseResource theBaseResource) {
065                return resourceHasTagWithSystem(theBaseResource, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS);
066        }
067
068        public static boolean isGoldenRecordRedirected(IBaseResource theBaseResource) {
069                return resourceHasTag(
070                                theBaseResource, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED);
071        }
072
073        private static boolean resourceHasTag(IBaseResource theBaseResource, String theSystem, String theCode) {
074                if (theBaseResource == null) {
075                        return false;
076                }
077                return theBaseResource.getMeta().getTag(theSystem, theCode) != null;
078        }
079
080        private static boolean resourceHasTagWithSystem(IBaseResource theBaseResource, @Nonnull String theSystem) {
081                if (theBaseResource == null) {
082                        return false;
083                }
084                return theBaseResource.getMeta().getTag().stream().anyMatch(tag -> theSystem.equalsIgnoreCase(tag.getSystem()));
085        }
086
087        private static Optional<? extends IBaseCoding> getTagWithSystem(
088                        IBaseResource theResource, @Nonnull String theSystem) {
089                return theResource.getMeta().getTag().stream()
090                                .filter(tag -> theSystem.equalsIgnoreCase(tag.getSystem()))
091                                .findFirst();
092        }
093
094        public static void removeTagWithSystem(IBaseResource theResource, @Nonnull String theSystem) {
095                theResource.getMeta().getTag().removeIf(tag -> theSystem.equalsIgnoreCase(tag.getSystem()));
096        }
097
098        /**
099         * Sets the MDM-managed tag, indicating the MDM system has ownership of this
100         * Resource. No changes are made if resource is already managed by MDM.
101         *
102         * @param theBaseResource resource to set the tag for
103         * @return Returns resource with the tag set.
104         */
105        public static IBaseResource setMdmManaged(IBaseResource theBaseResource) {
106                return setTagOnResource(
107                                theBaseResource,
108                                MdmConstants.SYSTEM_MDM_MANAGED,
109                                MdmConstants.CODE_HAPI_MDM_MANAGED,
110                                MdmConstants.DISPLAY_HAPI_MDM_MANAGED);
111        }
112
113        public static IBaseResource setGoldenResource(IBaseResource theBaseResource) {
114                return setTagOnResource(
115                                theBaseResource,
116                                MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS,
117                                MdmConstants.CODE_GOLDEN_RECORD,
118                                MdmConstants.DISPLAY_GOLDEN_RECORD);
119        }
120
121        /**
122         * Sets the provided resource as 'redirected' golden resource.
123         * This is done when a Golden Resource has been deprecated
124         * and is no longer the primary golden resource (for example,
125         * after a merge of 2 golden resources).
126         */
127        public static IBaseResource setGoldenResourceRedirected(IBaseResource theBaseResource) {
128                return setTagOnResource(
129                                theBaseResource,
130                                MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS,
131                                MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED,
132                                MdmConstants.DISPLAY_GOLDEN_REDIRECT);
133        }
134
135        /**
136         * Adds the BLOCKED tag to the golden resource.
137         * Because this is called *before* a resource is saved,
138         * we must add a new system/code combo to it
139         * @param theBaseResource
140         * @return
141         */
142        public static IBaseResource setGoldenResourceAsBlockedResourceGoldenResource(IBaseResource theBaseResource) {
143                IBaseCoding tag = theBaseResource.getMeta().addTag();
144                tag.setSystem(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS);
145                tag.setCode(MdmConstants.CODE_BLOCKED);
146                tag.setDisplay(MdmConstants.CODE_BLOCKED_DISPLAY);
147                tag.setUserSelected(false);
148                tag.setVersion("1");
149
150                return theBaseResource;
151        }
152
153        /**
154         * WARNING: This code may _look_ like it replaces in place a code of a tag, but this DOES NOT ACTUALLY WORK!. In reality what will
155         * happen is a secondary tag will be created with the same system. the only way to actually remove a tag from a resource
156         * is by calling dao.removeTag(). This logic here is for the case where our representation of the resource still happens to contain
157         * a reference to a tag, to make sure it isn't double-added.
158         */
159        @Nonnull
160        private static IBaseResource setTagOnResource(
161                        IBaseResource theGoldenResource, String theSystem, String theCode, String theDisplay) {
162                Optional<? extends IBaseCoding> tagWithSystem = getTagWithSystem(theGoldenResource, theSystem);
163                if (tagWithSystem.isPresent()) {
164                        tagWithSystem.get().setCode(theCode);
165                        tagWithSystem.get().setDisplay(theDisplay);
166                } else {
167                        IBaseCoding tag = theGoldenResource.getMeta().addTag();
168                        tag.setSystem(theSystem);
169                        tag.setCode(theCode);
170                        tag.setDisplay(theDisplay);
171                        tag.setUserSelected(false);
172                        tag.setVersion("1");
173                }
174                return theGoldenResource;
175        }
176}