001/*-
002 * #%L
003 * HAPI FHIR Storage api
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.subscription.model;
021
022import ca.uhn.fhir.context.FhirContext;
023import ca.uhn.fhir.model.api.IModelJson;
024import com.fasterxml.jackson.annotation.JsonProperty;
025import jakarta.annotation.Nonnull;
026import jakarta.annotation.Nullable;
027import org.apache.commons.lang3.builder.EqualsBuilder;
028import org.apache.commons.lang3.builder.HashCodeBuilder;
029import org.apache.commons.lang3.builder.ToStringBuilder;
030import org.hl7.fhir.instance.model.api.IIdType;
031import org.hl7.fhir.instance.model.api.IPrimitiveType;
032import org.hl7.fhir.r4.model.IdType;
033import org.hl7.fhir.r4.model.Subscription;
034
035import java.io.Serializable;
036import java.util.ArrayList;
037import java.util.Collections;
038import java.util.HashMap;
039import java.util.List;
040import java.util.Map;
041
042import static org.apache.commons.lang3.StringUtils.isNotBlank;
043
044public class CanonicalSubscription implements Serializable, Cloneable, IModelJson {
045
046        private static final long serialVersionUID = 1L;
047
048        @JsonProperty("id")
049        private String myIdElement;
050
051        @JsonProperty("criteria")
052        private String myCriteriaString;
053
054        @JsonProperty("endpointUrl")
055        private String myEndpointUrl;
056
057        @JsonProperty("payload")
058        private String myPayloadString;
059
060        @JsonProperty("headers")
061        private List<String> myHeaders;
062
063        @JsonProperty("channelType")
064        private CanonicalSubscriptionChannelType myChannelType;
065
066        @JsonProperty("status")
067        private Subscription.SubscriptionStatus myStatus;
068
069        @JsonProperty("triggerDefinition")
070        @Deprecated
071        private CanonicalEventDefinition myTrigger;
072
073        @JsonProperty("emailDetails")
074        private EmailDetails myEmailDetails;
075
076        @JsonProperty("restHookDetails")
077        private RestHookDetails myRestHookDetails;
078
079        @JsonProperty("extensions")
080        private Map<String, List<String>> myChannelExtensions;
081
082        @JsonProperty("tags")
083        private Map<String, String> myTags;
084
085        @JsonProperty("payloadSearchCriteria")
086        private String myPayloadSearchCriteria;
087
088        @JsonProperty("partitionId")
089        private Integer myPartitionId;
090
091        @JsonProperty("crossPartitionEnabled")
092        private boolean myCrossPartitionEnabled;
093
094        @JsonProperty("sendDeleteMessages")
095        private boolean mySendDeleteMessages;
096
097        @JsonProperty("isTopicSubscription")
098        private boolean myIsTopicSubscription;
099
100        @JsonProperty("myTopicSubscription")
101        private CanonicalTopicSubscription myTopicSubscription;
102
103        /**
104         * Constructor
105         */
106        public CanonicalSubscription() {
107                super();
108        }
109
110        public String getPayloadSearchCriteria() {
111                return myPayloadSearchCriteria;
112        }
113
114        public void setPayloadSearchCriteria(String thePayloadSearchCriteria) {
115                myPayloadSearchCriteria = thePayloadSearchCriteria;
116        }
117
118        @Deprecated
119        public void addTrigger(CanonicalEventDefinition theTrigger) {
120                myTrigger = theTrigger;
121        }
122
123        public CanonicalSubscriptionChannelType getChannelType() {
124                return myChannelType;
125        }
126
127        public void setChannelType(CanonicalSubscriptionChannelType theChannelType) {
128                myChannelType = theChannelType;
129        }
130
131        public String getCriteriaString() {
132                return myCriteriaString;
133        }
134
135        public void setCriteriaString(String theCriteriaString) {
136                myCriteriaString = theCriteriaString;
137        }
138
139        public EmailDetails getEmailDetails() {
140                if (myEmailDetails == null) {
141                        myEmailDetails = new EmailDetails();
142                }
143                return myEmailDetails;
144        }
145
146        public String getEndpointUrl() {
147                return myEndpointUrl;
148        }
149
150        public void setEndpointUrl(String theEndpointUrl) {
151                myEndpointUrl = theEndpointUrl;
152        }
153
154        @Nonnull
155        public List<String> getHeaders() {
156                return myHeaders != null ? Collections.unmodifiableList(myHeaders) : Collections.emptyList();
157        }
158
159        public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) {
160                myHeaders = new ArrayList<>();
161                for (IPrimitiveType<String> next : theHeader) {
162                        if (isNotBlank(next.getValueAsString())) {
163                                myHeaders.add(next.getValueAsString());
164                        }
165                }
166        }
167
168        public void setHeaders(String theHeaders) {
169                myHeaders = new ArrayList<>();
170                if (isNotBlank(theHeaders)) {
171                        myHeaders.add(theHeaders);
172                }
173        }
174
175        public Map<String, String> getTags() {
176                if (myTags == null) {
177                        myTags = new HashMap<>();
178                }
179                return myTags;
180        }
181
182        public void setTags(Map<String, String> theTags) {
183                this.myTags = theTags;
184        }
185
186        public String getChannelExtension(String theUrl) {
187                String retVal = null;
188                List<String> channelExtensions = myChannelExtensions.get(theUrl);
189                if (channelExtensions != null && !channelExtensions.isEmpty()) {
190                        retVal = channelExtensions.get(0);
191                }
192                return retVal;
193        }
194
195        @Nonnull
196        public List<String> getChannelExtensions(String theUrl) {
197                List<String> retVal = myChannelExtensions.get(theUrl);
198                if (retVal == null) {
199                        retVal = Collections.emptyList();
200                } else {
201                        retVal = Collections.unmodifiableList(retVal);
202                }
203                return retVal;
204        }
205
206        public void setChannelExtensions(Map<String, List<String>> theChannelExtensions) {
207                myChannelExtensions = new HashMap<>();
208                for (String url : theChannelExtensions.keySet()) {
209                        List<String> values = theChannelExtensions.get(url);
210                        if (isNotBlank(url) && values != null) {
211                                myChannelExtensions.put(url, values);
212                        }
213                }
214        }
215
216        @Nullable
217        public IIdType getIdElement(FhirContext theContext) {
218                IIdType retVal = null;
219                if (isNotBlank(myIdElement)) {
220                        retVal = theContext.getVersion().newIdType().setValue(myIdElement);
221                }
222                return retVal;
223        }
224
225        public String getIdPart() {
226                return new IdType(getIdElementString()).getIdPart();
227        }
228
229        public String getIdElementString() {
230                return myIdElement;
231        }
232
233        public String getPayloadString() {
234                return myPayloadString;
235        }
236
237        public void setPayloadString(String thePayloadString) {
238                myPayloadString = thePayloadString;
239        }
240
241        public RestHookDetails getRestHookDetails() {
242                if (myRestHookDetails == null) {
243                        myRestHookDetails = new RestHookDetails();
244                }
245                return myRestHookDetails;
246        }
247
248        public Subscription.SubscriptionStatus getStatus() {
249                return myStatus;
250        }
251
252        public void setStatus(Subscription.SubscriptionStatus theStatus) {
253                myStatus = theStatus;
254        }
255
256        public Integer getRequestPartitionId() {
257                return myPartitionId;
258        }
259
260        public void setPartitionId(Integer thePartitionId) {
261                myPartitionId = thePartitionId;
262        }
263
264        public boolean isCrossPartitionEnabled() {
265                return myCrossPartitionEnabled;
266        }
267
268        public void setCrossPartitionEnabled(boolean myCrossPartitionEnabled) {
269                this.myCrossPartitionEnabled = myCrossPartitionEnabled;
270        }
271
272        @Deprecated
273        public CanonicalEventDefinition getTrigger() {
274                return myTrigger;
275        }
276
277        public boolean getSendDeleteMessages() {
278                return mySendDeleteMessages;
279        }
280
281        public void setSendDeleteMessages(boolean theSendDeleteMessages) {
282                mySendDeleteMessages = theSendDeleteMessages;
283        }
284
285        @Override
286        public boolean equals(Object theO) {
287                if (this == theO) return true;
288
289                if (theO == null || getClass() != theO.getClass()) return false;
290
291                CanonicalSubscription that = (CanonicalSubscription) theO;
292
293                EqualsBuilder b = new EqualsBuilder();
294                b.append(myIdElement, that.myIdElement);
295                b.append(myCriteriaString, that.myCriteriaString);
296                b.append(myEndpointUrl, that.myEndpointUrl);
297                b.append(myPayloadString, that.myPayloadString);
298                b.append(myHeaders, that.myHeaders);
299                b.append(myChannelType, that.myChannelType);
300                b.append(myStatus, that.myStatus);
301                b.append(myTrigger, that.myTrigger);
302                b.append(myEmailDetails, that.myEmailDetails);
303                b.append(myRestHookDetails, that.myRestHookDetails);
304                b.append(myChannelExtensions, that.myChannelExtensions);
305                b.append(myCrossPartitionEnabled, that.myCrossPartitionEnabled);
306                b.append(myChannelExtensions, that.myChannelExtensions);
307                b.append(mySendDeleteMessages, that.mySendDeleteMessages);
308                b.append(myPayloadSearchCriteria, that.myPayloadSearchCriteria);
309                b.append(myTopicSubscription, that.myTopicSubscription);
310                return b.isEquals();
311        }
312
313        @Override
314        public int hashCode() {
315                return new HashCodeBuilder(17, 37)
316                                .append(myIdElement)
317                                .append(myCriteriaString)
318                                .append(myEndpointUrl)
319                                .append(myPayloadString)
320                                .append(myHeaders)
321                                .append(myChannelType)
322                                .append(myStatus)
323                                .append(myTrigger)
324                                .append(myEmailDetails)
325                                .append(myRestHookDetails)
326                                .append(myChannelExtensions)
327                                .toHashCode();
328        }
329
330        public void setIdElement(IIdType theIdElement) {
331                myIdElement = null;
332                if (theIdElement != null) {
333                        myIdElement = theIdElement.toUnqualifiedVersionless().getValue();
334                }
335        }
336
337        /**
338         * Adds a header
339         *
340         * @param theHeader The header, e.g. "Authorization: Bearer AAAAA"
341         */
342        public void addHeader(String theHeader) {
343                if (isNotBlank(theHeader)) {
344                        initHeaders();
345                        myHeaders.add(theHeader);
346                }
347        }
348
349        private void initHeaders() {
350                if (myHeaders == null) {
351                        myHeaders = new ArrayList<>();
352                }
353        }
354
355        @Override
356        public String toString() {
357                ToStringBuilder stringBuilder = new ToStringBuilder(this)
358                                .append("myIdElement", myIdElement)
359                                .append("myStatus", myStatus)
360                                .append("myCriteriaString", myCriteriaString);
361                //                      .append("myEndpointUrl", myEndpointUrl)
362                //                      .append("myPayloadString", myPayloadString)
363                //                      .append("myHeaders", myHeaders)
364                //                      .append("myChannelType", myChannelType)
365                //                      .append("myTrigger", myTrigger)
366                //                      .append("myEmailDetails", myEmailDetails)
367                //                      .append("myRestHookDetails", myRestHookDetails)
368                //                      .append("myChannelExtensions", myChannelExtensions)
369                if (isTopicSubscription()) {
370                        stringBuilder.append("topic", myTopicSubscription.getTopic());
371                } else {
372                        stringBuilder.append("criteriaString", myCriteriaString);
373                }
374
375                return stringBuilder.toString();
376        }
377
378        public void setTopicSubscription(boolean theTopicSubscription) {
379                myIsTopicSubscription = theTopicSubscription;
380        }
381
382        public boolean isTopicSubscription() {
383                return myIsTopicSubscription;
384        }
385
386        // PayloadString is called ContentType in R5
387        public String getContentType() {
388                assert isTopicSubscription();
389                return getPayloadString();
390        }
391
392        public CanonicalTopicSubscription getTopicSubscription() {
393                assert isTopicSubscription();
394                if (myTopicSubscription == null) {
395                        myTopicSubscription = new CanonicalTopicSubscription();
396                }
397                return myTopicSubscription;
398        }
399
400        public void setTopicSubscription(CanonicalTopicSubscription theTopicSubscription) {
401                myTopicSubscription = theTopicSubscription;
402        }
403
404        public org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent getContent() {
405                assert isTopicSubscription();
406                return myTopicSubscription.getContent();
407        }
408
409        public String getTopic() {
410                assert isTopicSubscription();
411                return myTopicSubscription.getTopic();
412        }
413
414        public List<CanonicalTopicSubscriptionFilter> getFilters() {
415                assert isTopicSubscription();
416                return myTopicSubscription.getFilters();
417        }
418
419        public int getHeartbeatPeriod() {
420                assert isTopicSubscription();
421                return myTopicSubscription.getHeartbeatPeriod();
422        }
423
424        public int getTimeout() {
425                assert isTopicSubscription();
426                return myTopicSubscription.getTimeout();
427        }
428
429        public int getMaxCount() {
430                assert isTopicSubscription();
431                return myTopicSubscription.getMaxCount();
432        }
433
434        public static class EmailDetails implements IModelJson {
435
436                @JsonProperty("from")
437                private String myFrom;
438
439                @JsonProperty("subjectTemplate")
440                private String mySubjectTemplate;
441
442                /**
443                 * Constructor
444                 */
445                public EmailDetails() {
446                        super();
447                }
448
449                public String getFrom() {
450                        return myFrom;
451                }
452
453                public void setFrom(String theFrom) {
454                        myFrom = theFrom;
455                }
456
457                public String getSubjectTemplate() {
458                        return mySubjectTemplate;
459                }
460
461                public void setSubjectTemplate(String theSubjectTemplate) {
462                        mySubjectTemplate = theSubjectTemplate;
463                }
464
465                @Override
466                public boolean equals(Object theO) {
467                        if (this == theO) return true;
468
469                        if (theO == null || getClass() != theO.getClass()) return false;
470
471                        EmailDetails that = (EmailDetails) theO;
472
473                        return new EqualsBuilder()
474                                        .append(myFrom, that.myFrom)
475                                        .append(mySubjectTemplate, that.mySubjectTemplate)
476                                        .isEquals();
477                }
478
479                @Override
480                public int hashCode() {
481                        return new HashCodeBuilder(17, 37)
482                                        .append(myFrom)
483                                        .append(mySubjectTemplate)
484                                        .toHashCode();
485                }
486        }
487
488        public static class RestHookDetails implements IModelJson {
489
490                @JsonProperty("stripVersionId")
491                private boolean myStripVersionId;
492
493                @JsonProperty("deliverLatestVersion")
494                private boolean myDeliverLatestVersion;
495
496                /**
497                 * Constructor
498                 */
499                public RestHookDetails() {
500                        super();
501                }
502
503                public boolean isDeliverLatestVersion() {
504                        return myDeliverLatestVersion;
505                }
506
507                public void setDeliverLatestVersion(boolean theDeliverLatestVersion) {
508                        myDeliverLatestVersion = theDeliverLatestVersion;
509                }
510
511                public boolean isStripVersionId() {
512                        return myStripVersionId;
513                }
514
515                public void setStripVersionId(boolean theStripVersionId) {
516                        myStripVersionId = theStripVersionId;
517                }
518
519                @Override
520                public boolean equals(Object theO) {
521                        if (this == theO) return true;
522
523                        if (theO == null || getClass() != theO.getClass()) return false;
524
525                        RestHookDetails that = (RestHookDetails) theO;
526
527                        return new EqualsBuilder()
528                                        .append(myStripVersionId, that.myStripVersionId)
529                                        .append(myDeliverLatestVersion, that.myDeliverLatestVersion)
530                                        .isEquals();
531                }
532
533                @Override
534                public int hashCode() {
535                        return new HashCodeBuilder(17, 37)
536                                        .append(myStripVersionId)
537                                        .append(myDeliverLatestVersion)
538                                        .toHashCode();
539                }
540        }
541
542        @Deprecated
543        public static class CanonicalEventDefinition implements IModelJson {
544
545                /**
546                 * Constructor
547                 */
548                @Deprecated
549                public CanonicalEventDefinition() {
550                        // nothing yet
551                }
552        }
553}