001/*-
002 * #%L
003 * HAPI FHIR JPA Model
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.model.config;
021
022import com.google.common.annotations.VisibleForTesting;
023import org.apache.commons.collections4.CollectionUtils;
024import org.hl7.fhir.dstu2.model.Subscription;
025
026import java.util.Collections;
027import java.util.HashSet;
028import java.util.Set;
029
030import static org.apache.commons.lang3.StringUtils.isNotBlank;
031
032public abstract class BaseSubscriptionSettings {
033        public static final String DEFAULT_EMAIL_FROM_ADDRESS = "noreply@unknown.com";
034        public static final String DEFAULT_WEBSOCKET_CONTEXT_PATH = "/websocket";
035        public static final String DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX =
036                        "((((http?|https?)://))([-%()_.!~*';/?:@&=+$,A-Za-z0-9])+)";
037
038        private final Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>();
039        private String myEmailFromAddress = DEFAULT_EMAIL_FROM_ADDRESS;
040        private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH;
041        private boolean myQualifySubscriptionMatchingChannelName = true;
042        private boolean myCrossPartitionSubscriptionEnabled = true;
043        private boolean myEnableInMemorySubscriptionMatching = true;
044        private boolean myTriggerSubscriptionsForNonVersioningChanges;
045
046        /**
047         * @since 6.8.0
048         * Prevents any non IN-MEMORY Search params from being created by users.
049         */
050        private boolean myAllowOnlyInMemorySubscriptions = false;
051
052        /**
053         * @since 7.6.0
054         *
055         * Regex To perform validation on the endpoint URL for Subscription of type RESTHOOK.
056         */
057        private String myRestHookEndpointUrlValidationRegex = DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX;
058
059        /**
060         * This setting indicates which subscription channel types are supported by the server.  Any subscriptions submitted
061         * to the server matching these types will be activated.
062         */
063        public void addSupportedSubscriptionType(Subscription.SubscriptionChannelType theSubscriptionChannelType) {
064                mySupportedSubscriptionTypes.add(theSubscriptionChannelType);
065        }
066
067        /**
068         * This setting indicates which subscription channel types are supported by the server.  Any subscriptions submitted
069         * to the server matching these types will be activated.
070         */
071        public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() {
072                return Collections.unmodifiableSet(mySupportedSubscriptionTypes);
073        }
074
075        /**
076         * Indicate whether a subscription channel type is supported by this server.
077         *
078         * @return true if at least one subscription channel type is supported by this server false otherwise.
079         */
080        public boolean hasSupportedSubscriptionTypes() {
081                return CollectionUtils.isNotEmpty(mySupportedSubscriptionTypes);
082        }
083
084        @VisibleForTesting
085        public void clearSupportedSubscriptionTypesForUnitTest() {
086                mySupportedSubscriptionTypes.clear();
087        }
088
089        /**
090         * If e-mail subscriptions are supported, the From address used when sending e-mails
091         */
092        public String getEmailFromAddress() {
093                return myEmailFromAddress;
094        }
095
096        /**
097         * Set the from address used for sending emails when using email subscriptions
098         */
099        public void setEmailFromAddress(String theEmailFromAddress) {
100                myEmailFromAddress = theEmailFromAddress;
101        }
102
103        /**
104         * If websocket subscriptions are enabled, this specifies the context path that listens to them.  Default value "/websocket".
105         */
106        public String getWebsocketContextPath() {
107                return myWebsocketContextPath;
108        }
109
110        /**
111         * Set the websocket endpoint context path to use when websocket subscriptions are enabled.  Default value "/websocket".
112         */
113        public void setWebsocketContextPath(String theWebsocketContextPath) {
114                myWebsocketContextPath = theWebsocketContextPath;
115        }
116
117        /**
118         * This setting returns whether the channel name should be qualified or not.
119         *
120         * @return whether the channel name is qualified or not
121         * @since 6.4.0
122         */
123        public boolean isQualifySubscriptionMatchingChannelName() {
124                return myQualifySubscriptionMatchingChannelName;
125        }
126
127        /**
128         * This setting controls whether the channel name
129         * should be qualified or not.
130         * Default is true, ie, the channel name will be qualified.
131         *
132         * @since 6.4.0
133         */
134        public void setQualifySubscriptionMatchingChannelName(boolean theQualifySubscriptionMatchingChannelName) {
135                myQualifySubscriptionMatchingChannelName = theQualifySubscriptionMatchingChannelName;
136        }
137
138        /**
139         * If enabled, the server will support cross-partition subscription.
140         * This subscription will be the responsible for all the requests from all the partitions on this server.
141         * For example, if the server has 3 partitions, P1, P2, P3
142         * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription.
143         * <p>
144         * Default is <code>false</code>
145         * </p>
146         *
147         * @since 5.7.0
148         */
149        public boolean isCrossPartitionSubscriptionEnabled() {
150                return myCrossPartitionSubscriptionEnabled;
151        }
152
153        /**
154         * If enabled, the server will support cross-partition subscription.
155         * This subscription will be the responsible for all the requests from all the partitions on this server.
156         * For example, if the server has 3 partitions, P1, P2, P3
157         * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription.
158         * <p>
159         * Default is <code>false</code>
160         * </p>
161         *
162         * @since 5.7.0
163         */
164        public void setCrossPartitionSubscriptionEnabled(boolean theAllowCrossPartitionSubscription) {
165                myCrossPartitionSubscriptionEnabled = theAllowCrossPartitionSubscription;
166        }
167        /**
168         * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve
169         * overall server performance.
170         *
171         * @since 6.8.0
172         */
173        public void setOnlyAllowInMemorySubscriptions(boolean theAllowOnlyInMemorySearchParams) {
174                myAllowOnlyInMemorySubscriptions = theAllowOnlyInMemorySearchParams;
175        }
176
177        /**
178         * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve
179         * overall server performance.
180         *
181         * @since 6.8.0
182         * @return Returns the value of {@link #setOnlyAllowInMemorySubscriptions(boolean)}
183         */
184        public boolean isOnlyAllowInMemorySubscriptions() {
185                return myAllowOnlyInMemorySubscriptions;
186        }
187
188        /**
189         * If set to <code>false</code> (default is true) the server will not use
190         * in-memory subscription searching and instead use the database matcher for all subscription
191         * criteria matching.
192         * <p>
193         * When there are subscriptions registered
194         * on the server, the default behaviour is to compare the changed resource to the
195         * subscription criteria directly in-memory without going out to the database.
196         * Certain types of subscription criteria, e.g. chained references of queries with
197         * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back
198         * to a database matcher.
199         * <p>
200         * The database matcher performs a query against the
201         * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity
202         *
203         * @since 3.6.1
204         */
205        public boolean isEnableInMemorySubscriptionMatching() {
206                return myEnableInMemorySubscriptionMatching;
207        }
208
209        /**
210         * If set to <code>false</code> (default is true) the server will not use
211         * in-memory subscription searching and instead use the database matcher for all subscription
212         * criteria matching.
213         * <p>
214         * When there are subscriptions registered
215         * on the server, the default behaviour is to compare the changed resource to the
216         * subscription criteria directly in-memory without going out to the database.
217         * Certain types of subscription criteria, e.g. chained references of queries with
218         * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back
219         * to a database matcher.
220         * <p>
221         * The database matcher performs a query against the
222         * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity
223         *
224         * @since 3.6.1
225         */
226        public void setEnableInMemorySubscriptionMatching(boolean theEnableInMemorySubscriptionMatching) {
227                myEnableInMemorySubscriptionMatching = theEnableInMemorySubscriptionMatching;
228        }
229
230        /**
231         * If set to true (default is false) then subscriptions will be triggered for resource updates even if they
232         * do not trigger a new version (e.g. $meta-add and $meta-delete).
233         *
234         * @since 5.5.0
235         */
236        public boolean isTriggerSubscriptionsForNonVersioningChanges() {
237                return myTriggerSubscriptionsForNonVersioningChanges;
238        }
239
240        /**
241         * If set to true (default is false) then subscriptions will be triggered for resource updates even if they
242         * do not trigger a new version (e.g. $meta-add and $meta-delete).
243         *
244         * @since 5.5.0
245         */
246        public void setTriggerSubscriptionsForNonVersioningChanges(boolean theTriggerSubscriptionsForNonVersioningChanges) {
247                myTriggerSubscriptionsForNonVersioningChanges = theTriggerSubscriptionsForNonVersioningChanges;
248        }
249
250        /**
251         * Provides the regex expression to perform endpoint URL validation If rest-hook subscriptions are supported.
252         * Default value is {@link #DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX}.
253         * @since 7.6.0
254         */
255        public String getRestHookEndpointUrlValidationRegex() {
256                return myRestHookEndpointUrlValidationRegex;
257        }
258
259        /**
260         * Configure the regex expression that will be used to validate the endpoint URL.
261         * Set to NULL or EMPTY for no endpoint URL validation.
262         *
263         * @since 7.6.0
264         */
265        public void setRestHookEndpointUrlValidationRegex(String theRestHookEndpointUrlValidationgRegex) {
266                myRestHookEndpointUrlValidationRegex = theRestHookEndpointUrlValidationgRegex;
267        }
268
269        /**
270         * Whether an endpoint validation Regex was set for URL validation.
271         *
272         * @since 7.6.0
273         */
274        public boolean hasRestHookEndpointUrlValidationRegex() {
275                return isNotBlank(myRestHookEndpointUrlValidationRegex);
276        }
277}