001/*-
002 * #%L
003 * HAPI FHIR Subscription Server
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.fhir.jpa.topic;
021
022import ca.uhn.fhir.cache.BaseResourceCacheSynchronizer;
023import ca.uhn.fhir.context.FhirContext;
024import ca.uhn.fhir.context.FhirVersionEnum;
025import ca.uhn.fhir.i18n.Msg;
026import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
027import ca.uhn.fhir.rest.param.TokenParam;
028import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
029import ca.uhn.fhir.subscription.SubscriptionConstants;
030import ca.uhn.fhir.util.Logs;
031import jakarta.annotation.Nonnull;
032import org.hl7.fhir.instance.model.api.IBaseResource;
033import org.hl7.fhir.r5.model.Enumerations;
034import org.hl7.fhir.r5.model.SubscriptionTopic;
035import org.slf4j.Logger;
036import org.springframework.beans.factory.annotation.Autowired;
037import org.springframework.context.event.ContextRefreshedEvent;
038import org.springframework.context.event.EventListener;
039
040import java.util.HashSet;
041import java.util.List;
042import java.util.Set;
043
044public class SubscriptionTopicLoader extends BaseResourceCacheSynchronizer {
045        private static final Logger ourLog = Logs.getSubscriptionTopicLog();
046
047        @Autowired
048        private FhirContext myFhirContext;
049
050        @Autowired
051        private SubscriptionTopicRegistry mySubscriptionTopicRegistry;
052
053        @Autowired
054        protected ISearchParamRegistry mySearchParamRegistry;
055
056        /**
057         * Constructor
058         */
059        public SubscriptionTopicLoader() {
060                super("SubscriptionTopic");
061        }
062
063        @Override
064        @EventListener(classes = ContextRefreshedEvent.class)
065        public void registerListener() {
066                if (!myFhirContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4B)) {
067                        return;
068                }
069                super.registerListener();
070        }
071
072        @Override
073        @Nonnull
074        protected SearchParameterMap getSearchParameterMap() {
075                SearchParameterMap map = new SearchParameterMap();
076
077                if (mySearchParamRegistry.getActiveSearchParam(
078                                                "SubscriptionTopic", "status", ISearchParamRegistry.SearchParamLookupContextEnum.ALL)
079                                != null) {
080                        map.add(SubscriptionTopic.SP_STATUS, new TokenParam(null, Enumerations.PublicationStatus.ACTIVE.toCode()));
081                }
082                map.setLoadSynchronousUpTo(SubscriptionConstants.MAX_SUBSCRIPTION_RESULTS);
083                return map;
084        }
085
086        @Override
087        protected void handleInit(List<IBaseResource> resourceList) {
088                updateSubscriptionTopicRegistry(resourceList);
089        }
090
091        @Override
092        protected int syncResourcesIntoCache(List<IBaseResource> resourceList) {
093                return updateSubscriptionTopicRegistry(resourceList);
094        }
095
096        private int updateSubscriptionTopicRegistry(List<IBaseResource> theResourceList) {
097                Set<String> allIds = new HashSet<>();
098                int registeredCount = 0;
099
100                for (IBaseResource resource : theResourceList) {
101                        String nextId = resource.getIdElement().getIdPart();
102                        allIds.add(nextId);
103
104                        boolean registered = mySubscriptionTopicRegistry.register(normalizeToR5(resource));
105                        if (registered) {
106                                registeredCount++;
107                        }
108                }
109
110                mySubscriptionTopicRegistry.unregisterAllIdsNotInCollection(allIds);
111                ourLog.debug("Finished sync subscription topics - registered {}", registeredCount);
112                return registeredCount;
113        }
114
115        private SubscriptionTopic normalizeToR5(IBaseResource theResource) {
116                if (theResource instanceof SubscriptionTopic) {
117                        return (SubscriptionTopic) theResource;
118                } else if (theResource instanceof org.hl7.fhir.r4b.model.SubscriptionTopic) {
119                        return SubscriptionTopicCanonicalizer.canonicalizeTopic(myFhirContext, theResource);
120                } else {
121                        throw new IllegalArgumentException(Msg.code(2332)
122                                        + "Only R4B and R5 SubscriptionTopic is currently supported.  Found " + theResource.getClass());
123                }
124        }
125}