
001/*- 002 * #%L 003 * HAPI FHIR - Core Library 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.util.bundle; 021 022import ca.uhn.fhir.context.BaseRuntimeChildDefinition; 023import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 024import ca.uhn.fhir.context.FhirContext; 025import ca.uhn.fhir.util.ElementUtil; 026import jakarta.annotation.Nonnull; 027import jakarta.annotation.Nullable; 028import org.hl7.fhir.instance.model.api.IBase; 029import org.hl7.fhir.instance.model.api.IBaseResource; 030import org.hl7.fhir.instance.model.api.IPrimitiveType; 031 032import java.util.Date; 033import java.util.Objects; 034import java.util.function.Function; 035 036import static ca.uhn.fhir.util.ElementUtil.getSingleValueOrNull; 037import static ca.uhn.fhir.util.ElementUtil.setValue; 038 039/** 040 * Components of a transaction-respose bundle entry. 041 */ 042public record BundleResponseEntryParts( 043 String fullUrl, 044 IBaseResource resource, 045 String responseStatus, 046 String responseLocation, 047 String responseEtag, 048 IPrimitiveType<Date> responseLastModified, 049 IBaseResource responseOutcome) { 050 051 static class Metadata implements PartsConverter<BundleResponseEntryParts> { 052 053 private final BaseRuntimeChildDefinition myFullUrlChildDef; 054 private final BaseRuntimeChildDefinition myResourceChildDef; 055 private final BaseRuntimeChildDefinition myResponseChildDef; 056 private final BaseRuntimeChildDefinition myResponseOutcomeChildDef; 057 private final BaseRuntimeChildDefinition myResponseStatusChildDef; 058 private final BaseRuntimeChildDefinition myResponseLocation; 059 private final BaseRuntimeChildDefinition myResponseEtag; 060 private final BaseRuntimeChildDefinition myResponseLastModified; 061 private final BaseRuntimeElementCompositeDefinition<?> myEntryElementDef; 062 private final BaseRuntimeElementCompositeDefinition<?> myResponseChildContentsDef; 063 064 private Metadata(FhirContext theFhirContext) { 065 066 BaseRuntimeChildDefinition entryChildDef = 067 theFhirContext.getResourceDefinition("Bundle").getChildByName("entry"); 068 069 myEntryElementDef = (BaseRuntimeElementCompositeDefinition<?>) entryChildDef.getChildByName("entry"); 070 071 myFullUrlChildDef = myEntryElementDef.getChildByName("fullUrl"); 072 myResourceChildDef = myEntryElementDef.getChildByName("resource"); 073 074 myResponseChildDef = myEntryElementDef.getChildByName("response"); 075 076 myResponseChildContentsDef = 077 (BaseRuntimeElementCompositeDefinition<?>) myResponseChildDef.getChildByName("response"); 078 079 myResponseOutcomeChildDef = myResponseChildContentsDef.getChildByName("outcome"); 080 myResponseStatusChildDef = myResponseChildContentsDef.getChildByName("status"); 081 myResponseLocation = myResponseChildContentsDef.getChildByName("location"); 082 myResponseEtag = myResponseChildContentsDef.getChildByName("etag"); 083 myResponseLastModified = myResponseChildContentsDef.getChildByName("lastModified"); 084 } 085 086 @Override 087 @Nullable 088 public BundleResponseEntryParts fromElement(IBase base) { 089 if (base == null) { 090 return null; 091 } 092 093 IBase response = getSingleValueOrNull(base, myResponseChildDef, Function.identity()); 094 095 return new BundleResponseEntryParts( 096 getSingleValueOrNull(base, myFullUrlChildDef, ElementUtil.CONVERT_PRIMITIVE_TO_STRING), 097 getSingleValueOrNull(base, myResourceChildDef, ElementUtil.CAST_BASE_TO_RESOURCE), 098 getSingleValueOrNull(response, myResponseStatusChildDef, ElementUtil.CONVERT_PRIMITIVE_TO_STRING), 099 getSingleValueOrNull(response, myResponseLocation, ElementUtil.CONVERT_PRIMITIVE_TO_STRING), 100 getSingleValueOrNull(response, myResponseEtag, ElementUtil.CONVERT_PRIMITIVE_TO_STRING), 101 getSingleValueOrNull(response, myResponseLastModified, ElementUtil.CAST_TO_PRIMITIVE_DATE), 102 getSingleValueOrNull(response, myResponseOutcomeChildDef, ElementUtil.CAST_BASE_TO_RESOURCE)); 103 } 104 105 @Override 106 public IBase toElement(BundleResponseEntryParts theParts) { 107 Objects.requireNonNull(theParts); 108 IBase entry = myEntryElementDef.newInstance(); 109 110 setValue(entry, myFullUrlChildDef, theParts.fullUrl()); 111 setValue(entry, myResourceChildDef, theParts.resource()); 112 113 // response parts 114 IBase response = myResponseChildContentsDef.newInstance(); 115 setValue(entry, myResponseChildDef, response); 116 117 setValue(response, myResponseStatusChildDef, theParts.responseStatus()); 118 setValue(response, myResponseLocation, theParts.responseLocation()); 119 setValue(response, myResponseEtag, theParts.responseEtag()); 120 setValue(response, myResponseLastModified, theParts.responseLastModified()); 121 setValue(response, myResponseOutcomeChildDef, theParts.responseOutcome()); 122 123 return entry; 124 } 125 } 126 127 /** 128 * Build an extractor function that can be used to extract the parts of a bundle entry. 129 * @param theFhirContext for the mappings 130 * @return an extractor function on IBase objects that returns a BundleResponseEntryParts object 131 */ 132 public static Function<IBase, BundleResponseEntryParts> buildPartsExtractor(FhirContext theFhirContext) { 133 PartsConverter<BundleResponseEntryParts> m = getConverter(theFhirContext); 134 return m::fromElement; 135 } 136 137 @Nonnull 138 public static PartsConverter<BundleResponseEntryParts> getConverter(FhirContext theFhirContext) { 139 return new Metadata(theFhirContext); 140 } 141 142 public static Function<BundleResponseEntryParts, IBase> builder(FhirContext theFhirContext) { 143 PartsConverter<BundleResponseEntryParts> m = getConverter(theFhirContext); 144 145 return m::toElement; 146 } 147}