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        public static final long DEFAULT_SUBMISSION_INTERVAL_IN_MS = 5000;
038
039        private final Set<Subscription.SubscriptionChannelType> mySupportedSubscriptionTypes = new HashSet<>();
040        private String myEmailFromAddress = DEFAULT_EMAIL_FROM_ADDRESS;
041        private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH;
042        private boolean myQualifySubscriptionMatchingChannelName = true;
043        private boolean myCrossPartitionSubscriptionEnabled = true;
044        private boolean myEnableInMemorySubscriptionMatching = true;
045        private boolean myTriggerSubscriptionsForNonVersioningChanges;
046        private long mySubmissionIntervalInMs = DEFAULT_SUBMISSION_INTERVAL_IN_MS;
047
048        /**
049         * @since 6.8.0
050         * Prevents any non IN-MEMORY Search params from being created by users.
051         */
052        private boolean myAllowOnlyInMemorySubscriptions = false;
053
054        /**
055         * If this is enabled (default is {@literal false}), changes to Subscription resource would be put on queue immediately.
056         * Reducing delay between creation of the Subscription and Activation.
057         *
058         * @since 7.8.0
059         */
060        private boolean mySubscriptionChangeQueuedImmediately = false;
061
062        /**
063         * @since 7.8.0
064         *
065         * Regex To perform validation on the endpoint URL for Subscription of type RESTHOOK.
066         */
067        private String myRestHookEndpointUrlValidationRegex = DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX;
068
069        /**
070         * This setting indicates which subscription channel types are supported by the server.  Any subscriptions submitted
071         * to the server matching these types will be activated.
072         */
073        public void addSupportedSubscriptionType(Subscription.SubscriptionChannelType theSubscriptionChannelType) {
074                mySupportedSubscriptionTypes.add(theSubscriptionChannelType);
075        }
076
077        /**
078         * This setting indicates which subscription channel types are supported by the server.  Any subscriptions submitted
079         * to the server matching these types will be activated.
080         */
081        public Set<Subscription.SubscriptionChannelType> getSupportedSubscriptionTypes() {
082                return Collections.unmodifiableSet(mySupportedSubscriptionTypes);
083        }
084
085        /**
086         * Indicate whether a subscription channel type is supported by this server.
087         *
088         * @return true if at least one subscription channel type is supported by this server false otherwise.
089         */
090        public boolean hasSupportedSubscriptionTypes() {
091                return CollectionUtils.isNotEmpty(mySupportedSubscriptionTypes);
092        }
093
094        @VisibleForTesting
095        public void clearSupportedSubscriptionTypesForUnitTest() {
096                mySupportedSubscriptionTypes.clear();
097        }
098
099        /**
100         * If e-mail subscriptions are supported, the From address used when sending e-mails
101         */
102        public String getEmailFromAddress() {
103                return myEmailFromAddress;
104        }
105
106        /**
107         * Set the from address used for sending emails when using email subscriptions
108         */
109        public void setEmailFromAddress(String theEmailFromAddress) {
110                myEmailFromAddress = theEmailFromAddress;
111        }
112
113        /**
114         * If websocket subscriptions are enabled, this specifies the context path that listens to them.  Default value "/websocket".
115         */
116        public String getWebsocketContextPath() {
117                return myWebsocketContextPath;
118        }
119
120        /**
121         * Set the websocket endpoint context path to use when websocket subscriptions are enabled.  Default value "/websocket".
122         */
123        public void setWebsocketContextPath(String theWebsocketContextPath) {
124                myWebsocketContextPath = theWebsocketContextPath;
125        }
126
127        /**
128         * This setting returns whether the channel name should be qualified or not.
129         *
130         * @return whether the channel name is qualified or not
131         * @since 6.4.0
132         */
133        public boolean isQualifySubscriptionMatchingChannelName() {
134                return myQualifySubscriptionMatchingChannelName;
135        }
136
137        /**
138         * This setting controls whether the channel name
139         * should be qualified or not.
140         * Default is true, ie, the channel name will be qualified.
141         *
142         * @since 6.4.0
143         */
144        public void setQualifySubscriptionMatchingChannelName(boolean theQualifySubscriptionMatchingChannelName) {
145                myQualifySubscriptionMatchingChannelName = theQualifySubscriptionMatchingChannelName;
146        }
147
148        /**
149         * If enabled, the server will support cross-partition subscription.
150         * This subscription will be the responsible for all the requests from all the partitions on this server.
151         * For example, if the server has 3 partitions, P1, P2, P3
152         * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription.
153         * <p>
154         * Default is <code>false</code>
155         * </p>
156         *
157         * @since 5.7.0
158         */
159        public boolean isCrossPartitionSubscriptionEnabled() {
160                return myCrossPartitionSubscriptionEnabled;
161        }
162
163        /**
164         * If enabled, the server will support cross-partition subscription.
165         * This subscription will be the responsible for all the requests from all the partitions on this server.
166         * For example, if the server has 3 partitions, P1, P2, P3
167         * The subscription will live in the DEFAULT partition. Resource posted to DEFAULT, P1, P2, and P3 will trigger this subscription.
168         * <p>
169         * Default is <code>false</code>
170         * </p>
171         *
172         * @since 5.7.0
173         */
174        public void setCrossPartitionSubscriptionEnabled(boolean theAllowCrossPartitionSubscription) {
175                myCrossPartitionSubscriptionEnabled = theAllowCrossPartitionSubscription;
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         */
183        public void setOnlyAllowInMemorySubscriptions(boolean theAllowOnlyInMemorySearchParams) {
184                myAllowOnlyInMemorySubscriptions = theAllowOnlyInMemorySearchParams;
185        }
186
187        /**
188         * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve
189         * overall server performance.
190         *
191         * @since 6.8.0
192         * @return Returns the value of {@link #setOnlyAllowInMemorySubscriptions(boolean)}
193         */
194        public boolean isOnlyAllowInMemorySubscriptions() {
195                return myAllowOnlyInMemorySubscriptions;
196        }
197
198        /**
199         * If set to <code>false</code> (default is true) the server will not use
200         * in-memory subscription searching and instead use the database matcher for all subscription
201         * criteria matching.
202         * <p>
203         * When there are subscriptions registered
204         * on the server, the default behaviour is to compare the changed resource to the
205         * subscription criteria directly in-memory without going out to the database.
206         * Certain types of subscription criteria, e.g. chained references of queries with
207         * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back
208         * to a database matcher.
209         * <p>
210         * The database matcher performs a query against the
211         * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity
212         *
213         * @since 3.6.1
214         */
215        public boolean isEnableInMemorySubscriptionMatching() {
216                return myEnableInMemorySubscriptionMatching;
217        }
218
219        /**
220         * If set to <code>false</code> (default is true) the server will not use
221         * in-memory subscription searching and instead use the database matcher for all subscription
222         * criteria matching.
223         * <p>
224         * When there are subscriptions registered
225         * on the server, the default behaviour is to compare the changed resource to the
226         * subscription criteria directly in-memory without going out to the database.
227         * Certain types of subscription criteria, e.g. chained references of queries with
228         * qualifiers or prefixes, are not supported by the in-memory matcher and will fall back
229         * to a database matcher.
230         * <p>
231         * The database matcher performs a query against the
232         * database by prepending ?id=XYZ to the subscription criteria where XYZ is the id of the changed entity
233         *
234         * @since 3.6.1
235         */
236        public void setEnableInMemorySubscriptionMatching(boolean theEnableInMemorySubscriptionMatching) {
237                myEnableInMemorySubscriptionMatching = theEnableInMemorySubscriptionMatching;
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 boolean isTriggerSubscriptionsForNonVersioningChanges() {
247                return myTriggerSubscriptionsForNonVersioningChanges;
248        }
249
250        /**
251         * If set to true (default is false) then subscriptions will be triggered for resource updates even if they
252         * do not trigger a new version (e.g. $meta-add and $meta-delete).
253         *
254         * @since 5.5.0
255         */
256        public void setTriggerSubscriptionsForNonVersioningChanges(boolean theTriggerSubscriptionsForNonVersioningChanges) {
257                myTriggerSubscriptionsForNonVersioningChanges = theTriggerSubscriptionsForNonVersioningChanges;
258        }
259
260        /**
261         * Provides the regex expression to perform endpoint URL validation If rest-hook subscriptions are supported.
262         * Default value is {@link #DEFAULT_RESTHOOK_ENDPOINTURL_VALIDATION_REGEX}.
263         * @since 7.6.0
264         */
265        public String getRestHookEndpointUrlValidationRegex() {
266                return myRestHookEndpointUrlValidationRegex;
267        }
268
269        /**
270         * Configure the regex expression that will be used to validate the endpoint URL.
271         * Set to NULL or EMPTY for no endpoint URL validation.
272         *
273         * @since 7.6.0
274         */
275        public void setRestHookEndpointUrlValidationRegex(String theRestHookEndpointUrlValidationgRegex) {
276                myRestHookEndpointUrlValidationRegex = theRestHookEndpointUrlValidationgRegex;
277        }
278
279        /**
280         * Whether an endpoint validation Regex was set for URL validation.
281         *
282         * @since 7.6.0
283         */
284        public boolean hasRestHookEndpointUrlValidationRegex() {
285                return isNotBlank(myRestHookEndpointUrlValidationRegex);
286        }
287
288        /**
289         * If this is enabled (default is {@literal false}), changes to Subscription resource would be put on queue immediately.
290         * Reducing delay between creation of the Subscription and Activation.
291         *
292         * @since 7.8.0
293         */
294        public boolean isSubscriptionChangeQueuedImmediately() {
295                return mySubscriptionChangeQueuedImmediately;
296        }
297
298        /**
299         * If this is enabled (default is {@literal false}), changes to Subscription resource would be put on queue immediately.
300         * Reducing delay between creation of the Subscription and Activation.
301         *
302         * @since 7.8.0
303         */
304        public void setSubscriptionChangeQueuedImmediately(boolean theSubscriptionChangeQueuedImmediately) {
305                mySubscriptionChangeQueuedImmediately = theSubscriptionChangeQueuedImmediately;
306        }
307
308        /**
309         * The interval in which the Resource Changes will be polled from DB. Defaults to {@literal 5000}.
310         *
311         * @since 7.7.0
312         */
313        public long getSubscriptionIntervalInMs() {
314                return mySubmissionIntervalInMs;
315        }
316
317        /**
318         * The interval in which the Resource Changes will be polled from DB. Defaults to {@literal 5000}.
319         *
320         * @since 7.7.0
321         */
322        public void setSubscriptionIntervalInMs(long theSubscriptionIntervalInMs) {
323                mySubmissionIntervalInMs = theSubscriptionIntervalInMs;
324        }
325}