001/*- 002 * #%L 003 * HAPI FHIR Subscription Server 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.jpa.topic; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.context.FhirVersionEnum; 024import ca.uhn.fhir.i18n.Msg; 025import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; 026import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; 027import ca.uhn.fhir.jpa.topic.status.INotificationStatusBuilder; 028import ca.uhn.fhir.jpa.topic.status.R4BNotificationStatusBuilder; 029import ca.uhn.fhir.jpa.topic.status.R4NotificationStatusBuilder; 030import ca.uhn.fhir.jpa.topic.status.R5NotificationStatusBuilder; 031import ca.uhn.fhir.rest.api.RestOperationTypeEnum; 032import ca.uhn.fhir.util.BundleBuilder; 033import org.apache.commons.lang3.ObjectUtils; 034import org.hl7.fhir.instance.model.api.IBaseBundle; 035import org.hl7.fhir.instance.model.api.IBaseResource; 036import org.hl7.fhir.r5.model.Bundle; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040import java.util.List; 041 042import static org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE; 043 044public class SubscriptionTopicPayloadBuilder { 045 private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionTopicPayloadBuilder.class); 046 private final FhirContext myFhirContext; 047 private final FhirVersionEnum myFhirVersion; 048 private final INotificationStatusBuilder<? extends IBaseResource> myNotificationStatusBuilder; 049 050 public SubscriptionTopicPayloadBuilder(FhirContext theFhirContext) { 051 myFhirContext = theFhirContext; 052 myFhirVersion = myFhirContext.getVersion().getVersion(); 053 054 switch (myFhirVersion) { 055 case R4: 056 myNotificationStatusBuilder = new R4NotificationStatusBuilder(myFhirContext); 057 break; 058 case R4B: 059 myNotificationStatusBuilder = new R4BNotificationStatusBuilder(myFhirContext); 060 break; 061 case R5: 062 myNotificationStatusBuilder = new R5NotificationStatusBuilder(myFhirContext); 063 break; 064 default: 065 throw unsupportedFhirVersionException(); 066 } 067 } 068 069 public IBaseBundle buildPayload( 070 List<IBaseResource> theResources, 071 ActiveSubscription theActiveSubscription, 072 String theTopicUrl, 073 RestOperationTypeEnum theRestOperationType) { 074 BundleBuilder bundleBuilder = new BundleBuilder(myFhirContext); 075 076 IBaseResource notificationStatus = 077 myNotificationStatusBuilder.buildNotificationStatus(theResources, theActiveSubscription, theTopicUrl); 078 bundleBuilder.addCollectionEntry(notificationStatus); 079 080 addResources(theResources, theActiveSubscription.getSubscription(), theRestOperationType, bundleBuilder); 081 // WIP STR5 add support for notificationShape include, revinclude 082 083 // Note we need to set the bundle type after we add the resources since adding the resources automatically sets 084 // the bundle type 085 setBundleType(bundleBuilder); 086 IBaseBundle retval = bundleBuilder.getBundle(); 087 if (ourLog.isDebugEnabled()) { 088 String bundle = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(retval); 089 ourLog.debug("Bundle: {}", bundle); 090 } 091 return retval; 092 } 093 094 private void addResources( 095 List<IBaseResource> theResources, 096 CanonicalSubscription theCanonicalSubscription, 097 RestOperationTypeEnum theRestOperationType, 098 BundleBuilder theBundleBuilder) { 099 100 org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent content = 101 ObjectUtils.defaultIfNull(theCanonicalSubscription.getContent(), FULLRESOURCE); 102 103 switch (content) { 104 case EMPTY: 105 // skip adding resource to the Bundle 106 break; 107 case IDONLY: 108 addIdOnly(theBundleBuilder, theResources, theRestOperationType); 109 break; 110 case FULLRESOURCE: 111 addFullResources(theBundleBuilder, theResources, theRestOperationType); 112 break; 113 } 114 } 115 116 private void addIdOnly( 117 BundleBuilder bundleBuilder, List<IBaseResource> theResources, RestOperationTypeEnum theRestOperationType) { 118 for (IBaseResource resource : theResources) { 119 switch (theRestOperationType) { 120 case CREATE: 121 bundleBuilder.addTransactionCreateEntryIdOnly(resource); 122 break; 123 case UPDATE: 124 bundleBuilder.addTransactionUpdateIdOnlyEntry(resource); 125 break; 126 case DELETE: 127 bundleBuilder.addTransactionDeleteEntry(resource); 128 break; 129 } 130 } 131 } 132 133 private void addFullResources( 134 BundleBuilder bundleBuilder, List<IBaseResource> theResources, RestOperationTypeEnum theRestOperationType) { 135 for (IBaseResource resource : theResources) { 136 switch (theRestOperationType) { 137 case CREATE: 138 bundleBuilder.addTransactionCreateEntry(resource); 139 break; 140 case UPDATE: 141 bundleBuilder.addTransactionUpdateEntry(resource); 142 break; 143 case DELETE: 144 bundleBuilder.addTransactionDeleteEntry(resource); 145 break; 146 } 147 } 148 } 149 150 private void setBundleType(BundleBuilder bundleBuilder) { 151 switch (myFhirVersion) { 152 case R4: 153 case R4B: 154 bundleBuilder.setType(Bundle.BundleType.HISTORY.toCode()); 155 break; 156 case R5: 157 bundleBuilder.setType(Bundle.BundleType.SUBSCRIPTIONNOTIFICATION.toCode()); 158 break; 159 default: 160 throw unsupportedFhirVersionException(); 161 } 162 } 163 164 private IllegalStateException unsupportedFhirVersionException() { 165 return new IllegalStateException( 166 Msg.code(2331) + "SubscriptionTopic subscriptions are not supported on FHIR version: " + myFhirVersion); 167 } 168}