001package ca.uhn.fhir.jpa.subscription.model; 002 003/*- 004 * #%L 005 * HAPI FHIR Storage api 006 * %% 007 * Copyright (C) 2014 - 2022 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.context.FhirContext; 024import ca.uhn.fhir.model.api.IModelJson; 025import com.fasterxml.jackson.annotation.JsonProperty; 026import org.apache.commons.lang3.builder.EqualsBuilder; 027import org.apache.commons.lang3.builder.HashCodeBuilder; 028import org.apache.commons.lang3.builder.ToStringBuilder; 029import org.hl7.fhir.instance.model.api.IIdType; 030import org.hl7.fhir.instance.model.api.IPrimitiveType; 031import org.hl7.fhir.r4.model.IdType; 032import org.hl7.fhir.r4.model.Subscription; 033 034import javax.annotation.Nonnull; 035import javax.annotation.Nullable; 036import java.io.Serializable; 037import java.util.ArrayList; 038import java.util.Collections; 039import java.util.HashMap; 040import java.util.List; 041import java.util.Map; 042 043import static org.apache.commons.lang3.StringUtils.isNotBlank; 044 045public class CanonicalSubscription implements Serializable, Cloneable, IModelJson { 046 047 private static final long serialVersionUID = 1L; 048 049 @JsonProperty("id") 050 private String myIdElement; 051 @JsonProperty("criteria") 052 private String myCriteriaString; 053 @JsonProperty("endpointUrl") 054 private String myEndpointUrl; 055 @JsonProperty("payload") 056 private String myPayloadString; 057 @JsonProperty("headers") 058 private List<String> myHeaders; 059 @JsonProperty("channelType") 060 private CanonicalSubscriptionChannelType myChannelType; 061 @JsonProperty("status") 062 private Subscription.SubscriptionStatus myStatus; 063 @JsonProperty("triggerDefinition") 064 private CanonicalEventDefinition myTrigger; 065 @JsonProperty("emailDetails") 066 private EmailDetails myEmailDetails; 067 @JsonProperty("restHookDetails") 068 private RestHookDetails myRestHookDetails; 069 @JsonProperty("extensions") 070 private Map<String, List<String>> myChannelExtensions; 071 @JsonProperty("tags") 072 private Map<String, String> myTags; 073 @JsonProperty("payloadSearchCriteria") 074 private String myPayloadSearchCriteria; 075 @JsonProperty("partitionId") 076 private Integer myPartitionId; 077 @JsonProperty("crossPartitionEnabled") 078 private boolean myCrossPartitionEnabled; 079 @JsonProperty("sendDeleteMessages") 080 private boolean mySendDeleteMessages; 081 082 /** 083 * Constructor 084 */ 085 public CanonicalSubscription() { 086 super(); 087 } 088 089 public String getPayloadSearchCriteria() { 090 return myPayloadSearchCriteria; 091 } 092 093 public void setPayloadSearchCriteria(String thePayloadSearchCriteria) { 094 myPayloadSearchCriteria = thePayloadSearchCriteria; 095 } 096 097 /** 098 * For now we're using the R4 TriggerDefinition, but this 099 * may change in the future when things stabilize 100 */ 101 public void addTrigger(CanonicalEventDefinition theTrigger) { 102 myTrigger = theTrigger; 103 } 104 105 public CanonicalSubscriptionChannelType getChannelType() { 106 return myChannelType; 107 } 108 109 public void setChannelType(CanonicalSubscriptionChannelType theChannelType) { 110 myChannelType = theChannelType; 111 } 112 113 public String getCriteriaString() { 114 return myCriteriaString; 115 } 116 117 public void setCriteriaString(String theCriteriaString) { 118 myCriteriaString = theCriteriaString; 119 } 120 121 public EmailDetails getEmailDetails() { 122 if (myEmailDetails == null) { 123 myEmailDetails = new EmailDetails(); 124 } 125 return myEmailDetails; 126 } 127 128 public String getEndpointUrl() { 129 return myEndpointUrl; 130 } 131 132 public void setEndpointUrl(String theEndpointUrl) { 133 myEndpointUrl = theEndpointUrl; 134 } 135 136 @Nonnull 137 public List<String> getHeaders() { 138 return myHeaders != null ? Collections.unmodifiableList(myHeaders) : Collections.emptyList(); 139 } 140 141 public void setHeaders(List<? extends IPrimitiveType<String>> theHeader) { 142 myHeaders = new ArrayList<>(); 143 for (IPrimitiveType<String> next : theHeader) { 144 if (isNotBlank(next.getValueAsString())) { 145 myHeaders.add(next.getValueAsString()); 146 } 147 } 148 } 149 150 public Map<String, String> getTags() { 151 return myTags; 152 } 153 154 public void setTags(Map<String, String> theTags) { 155 this.myTags = theTags; 156 } 157 158 159 public void setHeaders(String theHeaders) { 160 myHeaders = new ArrayList<>(); 161 if (isNotBlank(theHeaders)) { 162 myHeaders.add(theHeaders); 163 } 164 } 165 166 public String getChannelExtension(String theUrl) { 167 String retVal = null; 168 List<String> channelExtensions = myChannelExtensions.get(theUrl); 169 if (channelExtensions != null && !channelExtensions.isEmpty()) { 170 retVal = channelExtensions.get(0); 171 } 172 return retVal; 173 } 174 175 @Nonnull 176 public List<String> getChannelExtensions(String theUrl) { 177 List<String> retVal = myChannelExtensions.get(theUrl); 178 if (retVal == null) { 179 retVal = Collections.emptyList(); 180 } else { 181 retVal = Collections.unmodifiableList(retVal); 182 } 183 return retVal; 184 } 185 186 public void setChannelExtensions(Map<String, List<String>> theChannelExtensions) { 187 myChannelExtensions = new HashMap<>(); 188 for (String url : theChannelExtensions.keySet()) { 189 List<String> values = theChannelExtensions.get(url); 190 if (isNotBlank(url) && values != null) { 191 myChannelExtensions.put(url, values); 192 } 193 } 194 } 195 196 @Nullable 197 public IIdType getIdElement(FhirContext theContext) { 198 IIdType retVal = null; 199 if (isNotBlank(myIdElement)) { 200 retVal = theContext.getVersion().newIdType().setValue(myIdElement); 201 } 202 return retVal; 203 } 204 205 public String getIdPart() { 206 return new IdType(getIdElementString()).getIdPart(); 207 } 208 209 public String getIdElementString() { 210 return myIdElement; 211 } 212 213 public String getPayloadString() { 214 return myPayloadString; 215 } 216 217 public void setPayloadString(String thePayloadString) { 218 myPayloadString = thePayloadString; 219 } 220 221 public RestHookDetails getRestHookDetails() { 222 if (myRestHookDetails == null) { 223 myRestHookDetails = new RestHookDetails(); 224 } 225 return myRestHookDetails; 226 } 227 228 public Subscription.SubscriptionStatus getStatus() { 229 return myStatus; 230 } 231 232 public void setStatus(Subscription.SubscriptionStatus theStatus) { 233 myStatus = theStatus; 234 } 235 236 public Integer getRequestPartitionId() { 237 return myPartitionId; 238 } 239 240 public void setPartitionId(Integer thePartitionId) { 241 myPartitionId = thePartitionId; 242 } 243 244 public boolean getCrossPartitionEnabled() { 245 return myCrossPartitionEnabled; 246 } 247 248 public void setCrossPartitionEnabled(boolean myCrossPartitionEnabled) { 249 this.myCrossPartitionEnabled = myCrossPartitionEnabled; 250 } 251 252 /** 253 * For now we're using the R4 triggerdefinition, but this 254 * may change in the future when things stabilize 255 */ 256 public CanonicalEventDefinition getTrigger() { 257 return myTrigger; 258 } 259 260 public boolean getSendDeleteMessages() { 261 return mySendDeleteMessages; 262 } 263 264 public void setSendDeleteMessages(boolean theSendDeleteMessages) { 265 mySendDeleteMessages = theSendDeleteMessages; 266 } 267 268 @Override 269 public boolean equals(Object theO) { 270 if (this == theO) return true; 271 272 if (theO == null || getClass() != theO.getClass()) return false; 273 274 CanonicalSubscription that = (CanonicalSubscription) theO; 275 276 EqualsBuilder b = new EqualsBuilder(); 277 b.append(myIdElement, that.myIdElement); 278 b.append(myCriteriaString, that.myCriteriaString); 279 b.append(myEndpointUrl, that.myEndpointUrl); 280 b.append(myPayloadString, that.myPayloadString); 281 b.append(myHeaders, that.myHeaders); 282 b.append(myChannelType, that.myChannelType); 283 b.append(myStatus, that.myStatus); 284 b.append(myTrigger, that.myTrigger); 285 b.append(myEmailDetails, that.myEmailDetails); 286 b.append(myRestHookDetails, that.myRestHookDetails); 287 b.append(myChannelExtensions, that.myChannelExtensions); 288 return b.isEquals(); 289 } 290 291 @Override 292 public int hashCode() { 293 return new HashCodeBuilder(17, 37) 294 .append(myIdElement) 295 .append(myCriteriaString) 296 .append(myEndpointUrl) 297 .append(myPayloadString) 298 .append(myHeaders) 299 .append(myChannelType) 300 .append(myStatus) 301 .append(myTrigger) 302 .append(myEmailDetails) 303 .append(myRestHookDetails) 304 .append(myChannelExtensions) 305 .toHashCode(); 306 } 307 308 public void setIdElement(IIdType theIdElement) { 309 myIdElement = null; 310 if (theIdElement != null) { 311 myIdElement = theIdElement.toUnqualifiedVersionless().getValue(); 312 } 313 } 314 315 /** 316 * Adds a header 317 * 318 * @param theHeader The header, e.g. "Authorization: Bearer AAAAA" 319 */ 320 public void addHeader(String theHeader) { 321 if (isNotBlank(theHeader)) { 322 initHeaders(); 323 myHeaders.add(theHeader); 324 } 325 } 326 327 private void initHeaders() { 328 if (myHeaders == null) { 329 myHeaders = new ArrayList<>(); 330 } 331 } 332 333 @Override 334 public String toString() { 335 return new ToStringBuilder(this) 336 .append("myIdElement", myIdElement) 337 .append("myStatus", myStatus) 338 .append("myCriteriaString", myCriteriaString) 339 .append("myEndpointUrl", myEndpointUrl) 340 .append("myPayloadString", myPayloadString) 341// .append("myHeaders", myHeaders) 342 .append("myChannelType", myChannelType) 343// .append("myTrigger", myTrigger) 344// .append("myEmailDetails", myEmailDetails) 345// .append("myRestHookDetails", myRestHookDetails) 346// .append("myChannelExtensions", myChannelExtensions) 347 .toString(); 348 } 349 350 public static class EmailDetails implements IModelJson { 351 352 @JsonProperty("from") 353 private String myFrom; 354 @JsonProperty("subjectTemplate") 355 private String mySubjectTemplate; 356 357 /** 358 * Constructor 359 */ 360 public EmailDetails() { 361 super(); 362 } 363 364 public String getFrom() { 365 return myFrom; 366 } 367 368 public void setFrom(String theFrom) { 369 myFrom = theFrom; 370 } 371 372 public String getSubjectTemplate() { 373 return mySubjectTemplate; 374 } 375 376 public void setSubjectTemplate(String theSubjectTemplate) { 377 mySubjectTemplate = theSubjectTemplate; 378 } 379 380 @Override 381 public boolean equals(Object theO) { 382 if (this == theO) return true; 383 384 if (theO == null || getClass() != theO.getClass()) return false; 385 386 EmailDetails that = (EmailDetails) theO; 387 388 return new EqualsBuilder() 389 .append(myFrom, that.myFrom) 390 .append(mySubjectTemplate, that.mySubjectTemplate) 391 .isEquals(); 392 } 393 394 @Override 395 public int hashCode() { 396 return new HashCodeBuilder(17, 37) 397 .append(myFrom) 398 .append(mySubjectTemplate) 399 .toHashCode(); 400 } 401 } 402 403 public static class RestHookDetails implements IModelJson { 404 405 @JsonProperty("stripVersionId") 406 private boolean myStripVersionId; 407 @JsonProperty("deliverLatestVersion") 408 private boolean myDeliverLatestVersion; 409 410 /** 411 * Constructor 412 */ 413 public RestHookDetails() { 414 super(); 415 } 416 417 public boolean isDeliverLatestVersion() { 418 return myDeliverLatestVersion; 419 } 420 421 public void setDeliverLatestVersion(boolean theDeliverLatestVersion) { 422 myDeliverLatestVersion = theDeliverLatestVersion; 423 } 424 425 426 public boolean isStripVersionId() { 427 return myStripVersionId; 428 } 429 430 public void setStripVersionId(boolean theStripVersionId) { 431 myStripVersionId = theStripVersionId; 432 } 433 434 @Override 435 public boolean equals(Object theO) { 436 if (this == theO) return true; 437 438 if (theO == null || getClass() != theO.getClass()) return false; 439 440 RestHookDetails that = (RestHookDetails) theO; 441 442 return new EqualsBuilder() 443 .append(myStripVersionId, that.myStripVersionId) 444 .append(myDeliverLatestVersion, that.myDeliverLatestVersion) 445 .isEquals(); 446 } 447 448 @Override 449 public int hashCode() { 450 return new HashCodeBuilder(17, 37) 451 .append(myStripVersionId) 452 .append(myDeliverLatestVersion) 453 .toHashCode(); 454 } 455 456 } 457 458 public static class CanonicalEventDefinition implements IModelJson { 459 460 /** 461 * Constructor 462 */ 463 public CanonicalEventDefinition() { 464 // nothing yet 465 } 466 467 } 468}