001/*-
002 * #%L
003 * HAPI FHIR - Converter
004 * %%
005 * Copyright (C) 2014 - 2025 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.hapi.converters.canonical;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.subscription.SubscriptionConstants;
025import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50;
026import org.hl7.fhir.instance.model.api.IBaseResource;
027import org.hl7.fhir.r4.model.Basic;
028import org.hl7.fhir.r4.model.Extension;
029import org.hl7.fhir.r5.model.DateTimeType;
030import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
031import org.hl7.fhir.r5.model.SubscriptionTopic;
032import org.hl7.fhir.r5.model.SubscriptionTopic.InteractionTrigger;
033import org.hl7.fhir.r5.model.SubscriptionTopic.SubscriptionTopicCanFilterByComponent;
034import org.hl7.fhir.r5.model.SubscriptionTopic.SubscriptionTopicNotificationShapeComponent;
035import org.hl7.fhir.r5.model.SubscriptionTopic.SubscriptionTopicResourceTriggerComponent;
036
037public final class SubscriptionTopicCanonicalizer {
038        private SubscriptionTopicCanonicalizer() {}
039
040        public static SubscriptionTopic canonicalizeTopic(FhirContext theFhirContext, IBaseResource theSubscriptionTopic) {
041                switch (theFhirContext.getVersion().getVersion()) {
042                        case R4:
043                                if (theSubscriptionTopic instanceof Basic) {
044                                        return canonicalizeR4BasicTopic((Basic) theSubscriptionTopic);
045                                }
046                                throw new UnsupportedOperationException(
047                                                Msg.code(2337) + "Unsupported R4 resource type for subscription topic: "
048                                                                + theSubscriptionTopic.getClass().getSimpleName());
049                        case R4B:
050                                return (SubscriptionTopic) VersionConvertorFactory_43_50.convertResource(
051                                                (org.hl7.fhir.r4b.model.SubscriptionTopic) theSubscriptionTopic);
052                        case R5:
053                                return (SubscriptionTopic) theSubscriptionTopic;
054                        default:
055                                throw new UnsupportedOperationException(
056                                                Msg.code(2651) + "Subscription topics are not supported in FHIR version "
057                                                                + theFhirContext.getVersion().getVersion());
058                }
059        }
060
061        private static SubscriptionTopic canonicalizeR4BasicTopic(Basic theBasicTopic) {
062                SubscriptionTopic retVal = new SubscriptionTopic();
063
064                // Basic properties
065                retVal.setId(theBasicTopic.getIdElement().getIdPart());
066
067                // Extract regular extensions
068                for (Extension extension : theBasicTopic.getExtension()) {
069                        String url = extension.getUrl();
070
071                        if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_TOPIC_URL.equals(url)) {
072                                retVal.setUrl(extension.getValue().primitiveValue());
073                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_TOPIC_VERSION.equals(url)) {
074                                retVal.setVersion(extension.getValue().primitiveValue());
075                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_TOPIC_NAME.equals(url)) {
076                                retVal.setName(extension.getValue().primitiveValue());
077                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_TOPIC_TITLE.equals(url)) {
078                                retVal.setTitle(extension.getValue().primitiveValue());
079                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_TOPIC_DATE.equals(url)) {
080                                retVal.setDateElement(new DateTimeType(extension.getValue().primitiveValue()));
081                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_TOPIC_DESCRIPTION.equals(url)) {
082                                retVal.setDescription(extension.getValue().primitiveValue());
083                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_RESOURCE_TRIGGER.equals(url)) {
084                                processResourceTrigger(extension, retVal);
085                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_CAN_FILTER_BY.equals(url)) {
086                                processCanFilterBy(extension, retVal);
087                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_NOTIFICATION_SHAPE.equals(url)) {
088                                processNotificationShape(extension, retVal);
089                        }
090                }
091
092                // Extract modifier extensions (e.g., status)
093                for (Extension extension : theBasicTopic.getModifierExtension()) {
094                        String url = extension.getUrl();
095
096                        if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_TOPIC_STATUS.equals(url)) {
097                                String statusCode = extension.getValue().primitiveValue();
098                                retVal.setStatus(PublicationStatus.fromCode(statusCode));
099                        }
100                }
101
102                return retVal;
103        }
104
105        private static void processResourceTrigger(Extension theExtension, SubscriptionTopic theTopic) {
106                SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopicResourceTriggerComponent();
107
108                for (Extension ext : theExtension.getExtension()) {
109                        String url = ext.getUrl();
110
111                        if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_DESCRIPTION.equals(url)) {
112                                trigger.setDescription(ext.getValue().primitiveValue());
113                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_RESOURCE.equals(url)) {
114                                trigger.setResource(ext.getValue().primitiveValue());
115                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_SUPPORTED_INTERACTION.equals(url)) {
116                                trigger.addSupportedInteraction(
117                                                InteractionTrigger.fromCode(ext.getValue().primitiveValue()));
118                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_FHIRPATH_CRITERIA.equals(url)) {
119                                trigger.setFhirPathCriteria(ext.getValue().primitiveValue());
120                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_QUERY_CRITERIA.equals(url)) {
121                                // Process the queryCriteria extension
122                                processQueryCriteria(ext, trigger);
123                        }
124                }
125
126                theTopic.addResourceTrigger(trigger);
127        }
128
129        private static void processQueryCriteria(
130                        Extension theQueryCriteriaExtension, SubscriptionTopicResourceTriggerComponent theTrigger) {
131                // Create a queryCriteria backbone component for the ResourceTrigger
132                SubscriptionTopic.SubscriptionTopicResourceTriggerQueryCriteriaComponent queryCriteria =
133                                new SubscriptionTopic.SubscriptionTopicResourceTriggerQueryCriteriaComponent();
134
135                // Process each nested extension within the queryCriteria extension
136                for (Extension ext : theQueryCriteriaExtension.getExtension()) {
137                        String url = ext.getUrl();
138
139                        if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_QUERY_CRITERIA_PREVIOUS.equals(url)) {
140                                queryCriteria.setPrevious(ext.getValue().primitiveValue());
141                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_QUERY_CRITERIA_CURRENT.equals(url)) {
142                                queryCriteria.setCurrent(ext.getValue().primitiveValue());
143                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_QUERY_CRITERIA_REQUIRE_BOTH.equals(url)) {
144                                queryCriteria.setRequireBoth(Boolean.parseBoolean(ext.getValue().primitiveValue()));
145                        }
146                }
147
148                // Set the queryCriteria component on the trigger
149                theTrigger.setQueryCriteria(queryCriteria);
150        }
151
152        private static void processCanFilterBy(Extension theExtension, SubscriptionTopic theTopic) {
153                SubscriptionTopicCanFilterByComponent filterBy = new SubscriptionTopicCanFilterByComponent();
154
155                for (Extension ext : theExtension.getExtension()) {
156                        String url = ext.getUrl();
157
158                        if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_DESCRIPTION.equals(url)) {
159                                filterBy.setDescription(ext.getValue().primitiveValue());
160                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_RESOURCE.equals(url)) {
161                                filterBy.setResource(ext.getValue().primitiveValue());
162                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_FILTER_PARAMETER.equals(url)) {
163                                filterBy.setFilterParameter(ext.getValue().primitiveValue());
164                        }
165                }
166
167                theTopic.addCanFilterBy(filterBy);
168        }
169
170        private static void processNotificationShape(Extension theExtension, SubscriptionTopic theTopic) {
171                SubscriptionTopicNotificationShapeComponent shape = new SubscriptionTopicNotificationShapeComponent();
172
173                for (Extension ext : theExtension.getExtension()) {
174                        String url = ext.getUrl();
175
176                        if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_RESOURCE.equals(url)) {
177                                shape.setResource(ext.getValue().primitiveValue());
178                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_INCLUDE.equals(url)) {
179                                shape.addInclude(ext.getValue().primitiveValue());
180                        } else if (SubscriptionConstants.SUBSCRIPTION_TOPIC_R4_EXT_REVINCLUDE.equals(url)) {
181                                shape.addRevInclude(ext.getValue().primitiveValue());
182                        }
183                }
184
185                theTopic.addNotificationShape(shape);
186        }
187}