001/*-
002 * #%L
003 * HAPI FHIR Storage api
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.subscription.model;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.interceptor.model.RequestPartitionId;
024import ca.uhn.fhir.parser.IParser;
025import ca.uhn.fhir.rest.api.EncodingEnum;
026import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage;
027import ca.uhn.fhir.rest.server.messaging.IResourceMessage;
028import com.fasterxml.jackson.annotation.JsonIgnore;
029import com.fasterxml.jackson.annotation.JsonProperty;
030import jakarta.annotation.Nullable;
031import org.apache.commons.lang3.builder.ToStringBuilder;
032import org.hl7.fhir.instance.model.api.IBaseResource;
033import org.hl7.fhir.instance.model.api.IIdType;
034
035import java.util.Objects;
036
037import static org.apache.commons.lang3.StringUtils.isNotBlank;
038
039@SuppressWarnings("WeakerAccess")
040public class ResourceDeliveryMessage extends BaseResourceMessage implements IResourceMessage {
041
042        @JsonProperty("canonicalSubscription")
043        private CanonicalSubscription mySubscription;
044
045        @JsonProperty("partitionId")
046        private RequestPartitionId myPartitionId;
047
048        @JsonProperty("payload")
049        private String myPayloadString;
050
051        @JsonProperty("payloadId")
052        private String myPayloadId;
053
054        @JsonIgnore
055        private transient IBaseResource myPayloadDecoded;
056
057        /**
058         * Constructor
059         */
060        public ResourceDeliveryMessage() {
061                super();
062                myPartitionId = RequestPartitionId.defaultPartition();
063        }
064
065        public IBaseResource getPayload(FhirContext theCtx) {
066                IBaseResource retVal = myPayloadDecoded;
067                if (retVal == null && isNotBlank(myPayloadString)) {
068                        IParser parser = EncodingEnum.detectEncoding(myPayloadString).newParser(theCtx);
069                        retVal = parser.parseResource(myPayloadString);
070                        myPayloadDecoded = retVal;
071                }
072                return retVal;
073        }
074
075        public String getPayloadString() {
076                if (this.myPayloadString != null) {
077                        return this.myPayloadString;
078                }
079
080                return "";
081        }
082
083        public IIdType getPayloadId(FhirContext theCtx) {
084                IIdType retVal = null;
085                if (myPayloadId != null) {
086                        retVal = theCtx.getVersion().newIdType().setValue(myPayloadId);
087                }
088                return retVal;
089        }
090
091        public CanonicalSubscription getSubscription() {
092                return mySubscription;
093        }
094
095        public void setSubscription(CanonicalSubscription theSubscription) {
096                mySubscription = theSubscription;
097        }
098
099        public void setPayload(FhirContext theCtx, IBaseResource thePayload, EncodingEnum theEncoding) {
100                /*
101                 * Note that we populate the raw string but don't keep the parsed resource around when we set this. This
102                 * has two reasons:
103                 *  - If we build up a big queue of these on an in-memory queue, we aren't taking up double the memory
104                 *  - If use a serializing queue, we aren't behaving differently (and therefore possibly missing things
105                 *    in tests)
106                 */
107                myPayloadString = theEncoding.newParser(theCtx).encodeResourceToString(thePayload);
108                myPayloadId = thePayload.getIdElement().toUnqualifiedVersionless().getValue();
109        }
110
111        public void setPayloadToNull() {
112                myPayloadString = null;
113        }
114
115        @Override
116        public String getPayloadId() {
117                return myPayloadId;
118        }
119
120        @Override
121        public void setPayloadId(IIdType thePayloadId) {
122                myPayloadId = null;
123                if (thePayloadId != null) {
124                        myPayloadId = thePayloadId.getValue();
125                }
126        }
127
128        public RequestPartitionId getRequestPartitionId() {
129                return myPartitionId;
130        }
131
132        public void setPartitionId(RequestPartitionId thePartitionId) {
133                myPartitionId = thePartitionId;
134        }
135
136        @Override
137        public String toString() {
138                return new ToStringBuilder(this)
139                                .append("mySubscription", mySubscription == null ? "null" : mySubscription.getIdElementString())
140                                // it isn't safe to log payloads
141                                .append("myPayloadString", "[Not Logged]")
142                                .append("myPayload", myPayloadDecoded)
143                                .append("myPayloadId", myPayloadId)
144                                .append("myPartitionId", myPartitionId)
145                                .append("myOperationType", getOperationType())
146                                .toString();
147        }
148
149        /**
150         * Helper method to fetch the subscription ID
151         */
152        public String getSubscriptionId(FhirContext theFhirContext) {
153                String retVal = null;
154                if (getSubscription() != null) {
155                        IIdType idElement = getSubscription().getIdElement(theFhirContext);
156                        if (idElement != null) {
157                                retVal = idElement.getValue();
158                        }
159                }
160                return retVal;
161        }
162
163        @Nullable
164        @Override
165        public String getPayloadMessageKey() {
166                return Objects.toString(super.getPayloadMessageKey(), myPayloadId);
167        }
168}