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}