001/*-
002 * #%L
003 * Smile CDR - CDR
004 * %%
005 * Copyright (C) 2016 - 2024 Smile CDR, Inc.
006 * %%
007 * All rights reserved.
008 * #L%
009 */
010package ca.cdr.api.fhir.interceptor;
011
012import ca.cdr.api.fhirgw.json.AvailableRoutesJson;
013import ca.cdr.api.fhirgw.json.GatewayTargetJson;
014import ca.cdr.api.fhirgw.json.MatchedRoutesJson;
015import ca.cdr.api.fhirgw.model.CreateRequest;
016import ca.cdr.api.fhirgw.model.DeleteRequest;
017import ca.cdr.api.fhirgw.model.ISearchResultsAccumulator;
018import ca.cdr.api.fhirgw.model.OperationRequest;
019import ca.cdr.api.fhirgw.model.OperationResponse;
020import ca.cdr.api.fhirgw.model.ReadRequest;
021import ca.cdr.api.fhirgw.model.SearchPageRequest;
022import ca.cdr.api.fhirgw.model.SearchRequest;
023import ca.cdr.api.fhirgw.model.SearchResponse;
024import ca.cdr.api.fhirgw.model.TransactionRequest;
025import ca.cdr.api.fhirgw.model.UpdateRequest;
026import ca.cdr.api.model.json.ConvertedTransactionBundlesJson;
027import ca.cdr.api.model.json.IOAuth2ClientDetails;
028import ca.cdr.api.model.json.appgallery.console.AGConsoleJson;
029import ca.cdr.api.persistence.megascale.MegaScaleCredentialRequestJson;
030import ca.cdr.api.persistence.megascale.MegaScaleCredentialResponseJson;
031import ca.cdr.api.pub.cdaexchange.model.CdaToFhirConversionResultJson;
032import ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson;
033import ca.uhn.fhir.interceptor.api.IPointcut;
034import ca.uhn.fhir.rest.api.server.RequestDetails;
035import ca.uhn.fhir.rest.server.messaging.json.ResourceOperationJsonMessage;
036import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
037import ca.uhn.hl7v2.model.Message;
038import jakarta.annotation.Nonnull;
039import org.apache.commons.lang3.ArrayUtils;
040import org.hl7.fhir.instance.model.api.IBaseBundle;
041import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
042import org.hl7.fhir.instance.model.api.IBaseResource;
043
044import java.security.KeyStore;
045import java.util.Arrays;
046import java.util.Collections;
047import java.util.HashSet;
048import java.util.List;
049import java.util.Set;
050import java.util.stream.Collectors;
051
052/**
053 * Value for {@link CdrHook#value()}
054 * <p>
055 * Hook pointcuts are divided into several broad categories:
056 * <ul>
057 * <li>FHIRGW_xxx: Hooks on the FHIR Gateway module</li>
058 * </ul>
059 * </p>
060 */
061public enum CdrPointcut implements IPointcut {
062
063        /**
064         * <b>CDA pre-import Hook:</b>
065         * <p>This pointcut provides access to documents before they are imported via the $import-cda endpoint.</p>
066         * <p>Hooks may accept the following parameters:</p>
067         * <ul>
068         *    <li>{@link java.lang.String} - the document to be imported, in xml format.</li>
069         *    <li>{@link ca.cdr.api.pub.cdaexchange.model.CdaToFhirConversionResultJson} - object used to contain all
070         *    relevant data involved in the conversion of a CDA document to an IBaseBundle.</li>
071         * </ul>
072         * <p>Hooks should return the document after performing any modifications.</p>
073         */
074        CDA_PRE_IMPORT(String.class, String.class, CdaToFhirConversionResultJson.class),
075
076        /**
077         * <b>CDA post-import Hook:</b>
078         * <p>This pointcut provides access to documents after they are imported via the $import-cda endpoint.</p>
079         * <p>Hooks may accept the following parameters:</p>
080         * <ul>
081         *    <li>{@link IBaseOperationOutcome} - an operation outcome which can be used to track issues with the import.</li>
082         *    <li>{@link IBaseBundle} - the transaction bundle which can be modified by this Hook.</li>
083         *    <li>{@link java.lang.String} - the document that was imported, in xml format.</li>
084         *    <li>{@link ca.cdr.api.pub.cdaexchange.model.CdaToFhirConversionResultJson} - object used to contain all
085         *    relevant data involved in the conversion of a CDA document to an IBaseBundle.</li>
086         * </ul>
087         * <p>To modify the document before the {@link IBaseBundle} is generated, use the pre-import Hook instead.</p>
088         */
089        CDA_POST_IMPORT(
090                        void.class,
091                        IBaseOperationOutcome.class,
092                        IBaseBundle.class,
093                        String.class,
094                        CdaToFhirConversionResultJson.class),
095
096        /**
097         * <b>CDA post-export Hook:</b>
098         * <p>This pointcut provides access to documents after they are exported, and immediately
099         * before they are returned to the API client.</p>
100         * <p>Hooks may accept the following parameters:</p>
101         * <ul>
102         *    <li>{@link IBaseBundle} - the bundle associated with this export.</li>
103         *    <li>{@link java.lang.String} - the document being exported, in xml format.</li>
104         * </ul>
105         * <p>Hooks should return the document after performing any modifications. The resulting document
106         * is what will be returned to the API client.</p>
107         */
108        CDA_POST_EXPORT(String.class, IBaseBundle.class, String.class),
109
110        /**
111         * <b>Channel Import Hook:</b>
112         * The pointcut provides access to messages received on a broker queue.  It allows an interceptor to
113         * inspect, modify or mark for discard incoming messages before ingestion by the Channel Import module.
114         * <p>
115         * <p>
116         * Hooks may accept the following parameters:
117         * <ul>
118         * <li>
119         * {@link ResourceOperationJsonMessage} - Hook methods for this
120         * pointcut should take a single parameter of ResourceOperationJsonMessage as the input. This object
121         * contains the pre-processed channel import message.
122         * </li>
123         * </ul>
124         *
125         * </p>
126         * Hook methods must return a <code>Boolean</code> which indicates whether to process the message.
127         */
128        CHANNEL_IMPORT_MESSAGE_PRE_PROCESSED(Boolean.class, ResourceOperationJsonMessage.class),
129
130        /**
131         * <b>FHIR Gateway Hook:</b>
132         * This hook is called when the FHIR Gateway has just determined which routes should be invoked for
133         * a given FHIR <b>delete</b> operation. This hook is called once per client request, regardless of how many individual
134         * targets are eventually invoked against.
135         * <p>
136         * Hooks may accept the following parameters:
137         * <ul>
138         * <li>
139         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
140         * is about to be processed.
141         * </li>
142         * <li>
143         * {@link ca.cdr.api.fhirgw.model.DeleteRequest} - The delete that is about to be invoked. The hook method can modify this request, and modifications will affect all the operations that are performed against the target servers
144         * </li>
145         * <li>
146         * {@link ca.cdr.api.fhirgw.json.MatchedRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which have been selected for this request. Routes can be added or removed from this collection to affect which routes are invoked. They can be cast to their specific subclasses if necessary.
147         * </li>
148         * <li>
149         * {@link ca.cdr.api.fhirgw.json.AvailableRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which match the operation type of the request, e.g. read, create, delete, search, operation.
150         * </li>
151         * </ul>
152         * </p>
153         * Hook methods must return <code>void</code>.
154         *
155         */
156        FHIRGW_DELETE_POST_SELECT_ROUTE(
157                        void.class,
158                        ServletRequestDetails.class,
159                        DeleteRequest.class,
160                        MatchedRoutesJson.class,
161                        AvailableRoutesJson.class),
162
163        /**
164         * <b>FHIR Gateway Hook:</b>
165         * This hook is called when the FHIR Gateway has just determined which routes should be invoked for
166         * a given FHIR <b>update</b> operation. This hook is called once per client request, regardless of how many individual
167         * targets are eventually invoked against.
168         * <p>
169         * Hooks may accept the following parameters:
170         * <ul>
171         * <li>
172         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
173         * is about to be processed.
174         * </li>
175         * <li>
176         * {@link ca.cdr.api.fhirgw.model.UpdateRequest} - The update that is about to be invoked. The hook method can modify this request, and modifications will affect all the operations that are performed against the target servers
177         * </li>
178         * <li>
179         * {@link ca.cdr.api.fhirgw.json.MatchedRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which have been selected for this request. Routes can be added or removed from this collection to affect which routes are invoked. They can be cast to their specific subclasses if necessary.
180         * </li>
181         * <li>
182         * {@link ca.cdr.api.fhirgw.json.AvailableRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which match the operation type of the request, e.g. read, create, delete, search, operation.
183         * </li>
184         * </ul>
185         * </p>
186         * Hook methods must return <code>void</code>.
187         *
188         */
189        FHIRGW_UPDATE_POST_SELECT_ROUTE(
190                        void.class,
191                        ServletRequestDetails.class,
192                        UpdateRequest.class,
193                        MatchedRoutesJson.class,
194                        AvailableRoutesJson.class),
195
196        /**
197         * <b>FHIR Gateway Hook:</b>
198         * This hook is called when the FHIR Gateway has just determined which routes should be invoked for
199         * a given FHIR <b>operation</b> operation. This hook is called once per client request, regardless of how many individual
200         * targets are eventually invoked against.
201         * <p>
202         * Hooks may accept the following parameters:
203         * <ul>
204         * <li>
205         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
206         * is about to be processed.
207         * </li>
208         * <li>
209         * {@link ca.cdr.api.fhirgw.model.OperationRequest} - The operation that is about to be invoked. The hook method can modify this request, and modifications will affect all the operations that are performed against the target servers
210         * </li>
211         * <li>
212         * {@link ca.cdr.api.fhirgw.json.MatchedRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which have been selected for this request. Routes can be added or removed from this collection to affect which routes are invoked. They can be cast to their specific subclasses if necessary.
213         * </li>
214         * <li>
215         * {@link ca.cdr.api.fhirgw.json.AvailableRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which match the operation type of the request, e.g. read, create, delete, search, operation.
216         * </li>
217         * </ul>
218         * </p>
219         * Hook methods must return <code>void</code>.
220         *
221         */
222        FHIRGW_OPERATION_POST_SELECT_ROUTE(
223                        void.class,
224                        ServletRequestDetails.class,
225                        OperationRequest.class,
226                        MatchedRoutesJson.class,
227                        AvailableRoutesJson.class),
228
229        /**
230         * <b>FHIR Gateway Hook:</b>
231         * This hook is called when the FHIR Gateway has just determined which routes should be invoked for
232         * a given FHIR <b>search</b> operation. This hook is called once per client request, regardless of how many individual
233         * targets are eventually invoked against.
234         * <p>
235         * Hooks may accept the following parameters:
236         * <ul>
237         * <li>
238         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
239         * is about to be processed.
240         * </li>
241         * <li>
242         * {@link ca.cdr.api.fhirgw.model.SearchRequest} - The search that is about to be invoked. The hook method can modify this request, and modifications will affect all the operations that are performed against the target servers
243         * </li>
244         * <li>
245         * {@link ca.cdr.api.fhirgw.json.MatchedRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which have been selected for this request. Routes can be added or removed from this collection to affect which routes are invoked. They can be cast to their specific subclasses if necessary.
246         * </li>
247         * <li>
248         * {@link ca.cdr.api.fhirgw.json.AvailableRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which match the operation type of the request, e.g. read, create, delete, search, operation.
249         * </li>
250         * </ul>
251         * </p>
252         * Hook methods must return <code>void</code>.
253         *
254         */
255        FHIRGW_SEARCH_POST_SELECT_ROUTE(
256                        void.class,
257                        ServletRequestDetails.class,
258                        SearchRequest.class,
259                        MatchedRoutesJson.class,
260                        AvailableRoutesJson.class),
261
262        /**
263         * <b>FHIR Gateway Hook:</b>
264         * This hook is called when the FHIR Gateway has just determined which routes should be invoked for
265         * a given FHIR <b>create</b> operation. This hook is called once per client request, regardless of how many individual
266         * targets are eventually invoked against.
267         * <p>
268         * Hooks may accept the following parameters:
269         * <ul>
270         * <li>
271         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
272         * is about to be processed.
273         * </li>
274         * <li>
275         * {@link ca.cdr.api.fhirgw.model.CreateRequest} - The create that is about to be invoked. The hook method can modify this request, and modifications will affect all the operations that are performed against the target servers
276         * </li>
277         * <li>
278         * {@link ca.cdr.api.fhirgw.json.MatchedRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which have been selected for this request. Routes can be added or removed from this collection to affect which routes are invoked. They can be cast to their specific subclasses if necessary.
279         * </li>
280         * <li>
281         * {@link ca.cdr.api.fhirgw.json.AvailableRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which match the operation type of the request, e.g. read, create, delete, search, operation.
282         * </li>
283         * </ul>
284         * </p>
285         * Hook methods must return <code>void</code>.
286         *
287         */
288        FHIRGW_CREATE_POST_SELECT_ROUTE(
289                        void.class,
290                        ServletRequestDetails.class,
291                        CreateRequest.class,
292                        MatchedRoutesJson.class,
293                        AvailableRoutesJson.class),
294
295        /**
296         * <b>FHIR Gateway Hook:</b>
297         * This hook is called when the FHIR Gateway has just determined which routes should be invoked for
298         * a given FHIR <b>read</b> operation. This hook is called once per client request, regardless of how many individual
299         * targets are eventually invoked against.
300         * <p>
301         * Hooks may accept the following parameters:
302         * <ul>
303         * <li>
304         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
305         * is about to be processed.
306         * </li>
307         * <li>
308         * {@link ca.cdr.api.fhirgw.model.ReadRequest} - The read that is about to be invoked. The hook method can modify this request, and modifications will affect all the operations that are performed against the target servers
309         * </li>
310         * <li>
311         * {@link ca.cdr.api.fhirgw.json.MatchedRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which have been selected for this request. Routes can be added or removed from this collection to affect which routes are invoked. They can be cast to their specific subclasses if necessary.
312         * </li>
313         * <li>
314         * {@link ca.cdr.api.fhirgw.json.AvailableRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which match the operation type of the request, e.g. read, create, delete, search, operation.
315         * </li>
316         * </ul>
317         * </p>
318         * Hook methods must return <code>void</code>.
319         *
320         */
321        FHIRGW_READ_POST_SELECT_ROUTE(
322                        void.class,
323                        ServletRequestDetails.class,
324                        ReadRequest.class,
325                        MatchedRoutesJson.class,
326                        AvailableRoutesJson.class),
327
328        /**
329         * <b>FHIR Gateway Hook:</b>
330         * This hook is called when the FHIR Gateway has just determined which routes should be invoked for
331         * a given FHIR <b>transaction</b> operation. This hook is called once per client request, regardless of how many individual
332         * targets are eventually invoked against.
333         * <p>
334         * Hooks may accept the following parameters:
335         * <ul>
336         * <li>
337         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
338         * is about to be processed.
339         * </li>
340         * <li>
341         * {@link ca.cdr.api.fhirgw.model.TransactionRequest} - The transaction that is about to be invoked. The hook method can modify this request, and modifications will affect all the operations that are performed against the target servers.
342         * </li>
343         * <li>
344         * {@link ca.cdr.api.fhirgw.json.MatchedRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which have been selected for this request. Routes can be added or removed from this collection to affect which routes are invoked. They can be cast to their specific subclasses if necessary.
345         * </li>
346         * <li>
347         * {@link ca.cdr.api.fhirgw.json.AvailableRoutesJson} - A collection of {@link ca.cdr.api.fhirgw.json.IBaseRouteJson} which match the operation type of the request, e.g. read, create, delete, search, operation.
348         * </li>
349         * </ul>
350         * </p>
351         * Hook methods must return <code>void</code>.
352         *
353         */
354        FHIRGW_TRANSACTION_POST_SELECT_ROUTE(
355                        void.class,
356                        ServletRequestDetails.class,
357                        TransactionRequest.class,
358                        MatchedRoutesJson.class,
359                        AvailableRoutesJson.class),
360
361        /**
362         * <b>FHIR Gateway Hook:</b>
363         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>read</b> or <b>vread</b> operation against an individual
364         * target server. This hook is called once for each target that will be called, so if a single client read is being
365         * multicasted against two target servers, this hook will be invoked twice.
366         * <p>
367         * Hooks may accept the following parameters:
368         * <ul>
369         * <li>
370         * {@link ca.cdr.api.fhirgw.model.ReadRequest} - The read that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
371         * </li>
372         * <li>
373         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
374         * </li>
375         * <li>
376         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
377         * is about to be processed.
378         * </li>
379         * </ul>
380         * </p>
381         * Hook methods must return <code>void</code>.
382         */
383        FHIRGW_READ_TARGET_PREINVOKE(void.class, ReadRequest.class, GatewayTargetJson.class, ServletRequestDetails.class),
384
385        /**
386         * <b>FHIR Gateway Hook:</b>
387         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>operation</b> operation against an individual
388         * target server.
389         * <p>
390         * Hooks may accept the following parameters:
391         * <ul>
392         * <li>
393         * {@link ca.cdr.api.fhirgw.model.OperationRequest} - The read that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
394         * </li>
395         * <li>
396         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
397         * </li>
398         * <li>
399         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
400         * is about to be processed.
401         * </li>
402         * </ul>
403         * </p>
404         * Hook methods must return <code>void</code>.
405         */
406        FHIRGW_OPERATION_TARGET_PREINVOKE(
407                        void.class, OperationRequest.class, GatewayTargetJson.class, ServletRequestDetails.class),
408
409        /**
410         * <b>FHIR Gateway Hook:</b>
411         * This hook is called when the FHIR Gateway has finished invoking a FHIR <b>extended operation</b> operation against an individual
412         * target server. This hook is called once for each target that has been called, so if a single client operation is being
413         * multicasted against two target servers, this hook will be invoked twice.
414         * <p>
415         * <p>
416         * Hooks may accept the following parameters:
417         * <ul>
418         * <li>
419         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
420         * </li>
421         * <li>
422         * {@link ISearchResultsAccumulator} - The accumulator being used to collect the search results so far. This may be empty in the case of operations
423         * which do not return search results. Some operations, such as <b>$everything</b>, will return search results, but others such as <b>$</b>
424         * Hook methods may use this object to inspect results received by other endpoints when searching in serial mode, and can
425         * modify the results as needed. Note that the {@link #FHIRGW_SEARCH_TARGET_POSTINVOKE} pointcut is invoked once for each gateway
426         * target, <b>before</b> the search results are added to the accumulator. Results from the current target are found in the
427         * {@link SearchResponse} object, and will be moved from that object into the accumulator after this pointcut is complete.
428         * </li>
429         * <li>
430         * {@link ca.cdr.api.fhirgw.model.OperationResponse} - This object contains the Operation Response from the individual Gateway Target that was called. Interceptors may modify this object in any way they want. This may be null if the operation returns a Bundle (check the SearchResultsAccumulator instead).
431         * </li>
432         * </ul>
433         * </p>
434         * Hook methods must return <code>void</code>.
435         *
436         **/
437        FHIRGW_OPERATION_TARGET_POSTINVOKE(
438                        void.class,
439                        OperationRequest.class,
440                        ISearchResultsAccumulator.class,
441                        OperationResponse.class,
442                        GatewayTargetJson.class,
443                        ServletRequestDetails.class),
444
445        /**
446         * <b>FHIR Gateway Hook:</b>
447         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>search</b> operation against an individual
448         * target server. This hook is called once for each target that will be called, so if a single client search is being
449         * multicasted against two target servers, this hook will be invoked twice.
450         * <p>
451         * This hook can be contrasted with {@link #FHIRGW_SEARCH_PAGE_TARGET_PREINVOKE}:
452         * <ul>
453         *    <li>{@link #FHIRGW_SEARCH_TARGET_PREINVOKE} is called before the initial search is performed (which should return search results as well as paging links)</li>
454         *    <li>{@link #FHIRGW_SEARCH_PAGE_TARGET_PREINVOKE} is called before the subsequent pages of results are fetched</li>
455         * </ul>
456         * </p>
457         * <p>
458         * Hooks may accept the following parameters:
459         * <ul>
460         * <li>
461         * {@link ca.cdr.api.fhirgw.model.SearchRequest} - The search that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
462         * </li>
463         * <li>
464         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
465         * </li>
466         * <li>
467         * {@link ISearchResultsAccumulator} - The accumulator being used to collect the search results so far. Hook methods may use this object to inspect results recieved by other endpoints when searching in serial mode, and can modify the results as needed.
468         * </li>
469         * <li>
470         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
471         * is about to be processed.
472         * </li>
473         * </ul>
474         * </p>
475         * Hook methods must return <code>void</code>.
476         */
477        FHIRGW_SEARCH_TARGET_PREINVOKE(
478                        void.class,
479                        SearchRequest.class,
480                        GatewayTargetJson.class,
481                        ISearchResultsAccumulator.class,
482                        ServletRequestDetails.class),
483
484        /**
485         * <b>FHIR Gateway Hook:</b>
486         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>search</b> paging operation against an individual
487         * target server. This hook is called once for each target that will be called, so if a single client search is being
488         * multicasted against two target servers, this hook will be invoked twice.
489         * <p>
490         * This hook can be contrasted with {@link #FHIRGW_SEARCH_TARGET_PREINVOKE}:
491         * <ul>
492         *    <li>{@link #FHIRGW_SEARCH_TARGET_PREINVOKE} is called before the initial search is performed (which should return search results as well as paging links)</li>
493         *    <li>{@link #FHIRGW_SEARCH_PAGE_TARGET_PREINVOKE} is called before the subsequent pages of results are fetched</li>
494         * </ul>
495         * </p>
496         * <p>
497         * Hooks may accept the following parameters:
498         * <ul>
499         * <li>
500         * {@link ca.cdr.api.fhirgw.model.SearchPageRequest} - The search that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
501         * </li>
502         * <li>
503         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
504         * </li>
505         * <li>
506         * {@link ISearchResultsAccumulator} - The accumulator being used to collect the search results so far. Hook methods may use this object to inspect results received by other endpoints when searching in serial mode, and can modify the results as needed.
507         * </li>
508         * <li>
509         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
510         * is about to be processed.
511         * </li>
512         * </ul>
513         * </p>
514         * Hook methods must return <code>void</code>.
515         */
516        FHIRGW_SEARCH_PAGE_TARGET_PREINVOKE(
517                        void.class,
518                        SearchPageRequest.class,
519                        GatewayTargetJson.class,
520                        ISearchResultsAccumulator.class,
521                        ServletRequestDetails.class),
522
523        /**
524         * <b>FHIR Gateway Hook:</b>
525         * This hook is called when the FHIR Gateway has finished invoking a FHIR <b>search</b> operation against an individual
526         * target server. This hook is called once for each target that has been called, so if a single client search is being
527         * multicasted against two target servers, this hook will be invoked twice.
528         * <p>
529         * <p>
530         * Hooks may accept the following parameters:
531         * <ul>
532         * <li>
533         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
534         * </li>
535         * <li>
536         * {@link ISearchResultsAccumulator} - The accumulator being used to collect the search results so far.
537         * Hook methods may use this object to inspect results received by other endpoints when searching in serial mode, and can
538         * modify the results as needed. Note that the {@link #FHIRGW_SEARCH_TARGET_POSTINVOKE} pointcut is invoked once for each gateway
539         * target, <b>before</b> the search results are added to the accumulator. Results from the current target are found in the
540         * {@link SearchResponse} object, and will be moved from that object into the accumulator after this pointcut is complete.
541         * </li>
542         * <li>
543         * {@link ca.cdr.api.fhirgw.model.SearchResponse} - This object contains the search results from the individual Gateway Target that was called. Interceptors may modify this object in any way they want.
544         * </li>
545         * </ul>
546         * </p>
547         * Hook methods must return <code>void</code>.
548         */
549        FHIRGW_SEARCH_TARGET_POSTINVOKE(
550                        void.class,
551                        GatewayTargetJson.class,
552                        ISearchResultsAccumulator.class,
553                        SearchResponse.class,
554                        ServletRequestDetails.class),
555
556        /**
557         * <b>FHIR Gateway Hook:</b>
558         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>create</b> operation against an individual
559         * target server. This hook is called once for each target that will be called, so if a single client create is being
560         * multicasted against two target servers, this hook will be invoked twice.
561         * <p>
562         * For creates where a client id is specified, the <b>update</b> hook will be fired instead.
563         * </p>
564         * <p>
565         * Hooks may accept the following parameters:
566         * <ul>
567         * <li>
568         * {@link ca.cdr.api.fhirgw.model.CreateRequest} - The create that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
569         * </li>
570         * <li>
571         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
572         * </li>
573         * <li>
574         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
575         * is about to be processed.
576         * </li>
577         * </ul>
578         * </p>
579         * Hook methods must return <code>void</code>.
580         */
581        FHIRGW_CREATE_TARGET_PREINVOKE(
582                        void.class, CreateRequest.class, GatewayTargetJson.class, ServletRequestDetails.class),
583
584        /**
585         * <b>FHIR Gateway Hook:</b>
586         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>transaction</b> operation against an individual
587         * target server. This hook is called once for each target that will be called, so if a single client create is being
588         * multicasted against two target servers, this hook will be invoked twice.
589         * <p>
590         * For creates where a client id is specified, the <b>update</b> hook will be fired instead.
591         * </p>
592         * <p>
593         * Hooks may accept the following parameters:
594         * <ul>
595         * <li>
596         * {@link ca.cdr.api.fhirgw.model.TransactionRequest} - The transaction that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
597         * </li>
598         * <li>
599         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
600         * </li>
601         * <li>
602         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
603         * is about to be processed.
604         * </li>
605         * </ul>
606         * </p>
607         * Hook methods must return <code>void</code>.
608         */
609        FHIRGW_TRANSACTION_TARGET_PREINVOKE(
610                        void.class, TransactionRequest.class, GatewayTargetJson.class, ServletRequestDetails.class),
611
612        /**
613         * <b>FHIR Gateway Hook:</b>
614         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>update</b> operation against an individual
615         * target server. This hook is called once for each target that will be called, so if a single client update is being
616         * multicasted against two target servers, this hook will be invoked twice.
617         * <p>
618         * This hook will also be called for <b>create</b> operations when a client id is provided.
619         * </p>
620         * <p>
621         * Hooks may accept the following parameters:
622         * <ul>
623         * <li>
624         * {@link ca.cdr.api.fhirgw.model.UpdateRequest} - The update that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
625         * </li>
626         * <li>
627         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
628         * </li>
629         * <li>
630         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
631         * is about to be processed.
632         * </li>
633         * </ul>
634         * </p>
635         * Hook methods must return <code>void</code>.
636         */
637        FHIRGW_UPDATE_TARGET_PREINVOKE(
638                        void.class, UpdateRequest.class, GatewayTargetJson.class, ServletRequestDetails.class),
639
640        /**
641         * <b>FHIR Gateway Hook:</b>
642         * This hook is called when the FHIR Gateway is about to invoke a FHIR <b>delete</b> operation against an individual
643         * target server. This hook is called once for each target that will be called, so if a single client delete is being
644         * multicasted against two target servers, this hook will be invoked twice.
645         * <p>
646         * Hooks may accept the following parameters:
647         * <ul>
648         * <li>
649         * {@link ca.cdr.api.fhirgw.model.DeleteRequest} - The delete that is about to be invoked. The hook method can modify this request, and modifications will affect the operation that is actually performed against the target server.
650         * </li>
651         * <li>
652         * {@link ca.cdr.api.fhirgw.json.GatewayTargetJson} - The gateway target server definition. Hook methods should not modify this object, and any changes will be ignored.
653         * </li>
654         * <li>
655         * {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails} - A bean containing details about the request that
656         * is about to be processed.
657         * </li>
658         * </ul>
659         * </p>
660         * Hook methods must return <code>void</code>.
661         */
662        FHIRGW_DELETE_TARGET_PREINVOKE(
663                        void.class, DeleteRequest.class, GatewayTargetJson.class, ServletRequestDetails.class),
664
665        /**
666         * <b>HL7v2 Hook:</b>
667         * This hook is the first one called when a HL7v2 endpoint processes incoming messages.  It is invoked before
668         * the HL7v2 to FHIR mapping takes place for each incoming request. It may be used to provide
669         * alternate handling for some requests, screen requests before they are handled, alter the incoming message,etc.
670         * <p>
671         * Note that any exceptions thrown by this method will not be trapped by HAPI (they will be passed up to the server)
672         * </p>
673         * <p>
674         * Hooks may accept the following parameters:
675         * <ul>
676         * <li>
677         * {@link ca.uhn.hl7v2.model.Message} - the message that is about to be processed. The hook method can modify this
678         * message and modifications will affect the end result of processing the message by this server.
679         * <br/>
680         * <b>NOTE:</b> This parameter is deprecated. Use {@link Hl7v2ToFhirConversionResultJson#getModifiableMessage()}
681         * or {@link Hl7v2ToFhirConversionResultJson#setModifiableMessage(Message)} instead.
682         * </li>
683         * <li>
684         * {@link Hl7v2ToFhirConversionResultJson} - Contains all relevant data involved in the conversion of an HL7 v2.x
685         * message to a list of IBaseBundle resources.
686         * </li>
687         * </ul>
688         * </p>
689         * Hook methods may return <code>true</code> or <code>void</code> if processing should continue normally.
690         * This is generally the right thing to do. If your interceptor is processing the response rather than
691         * letting HAPI do it, you must return <code>false</code>. In this case,
692         * no further processing will occur.
693         */
694        HL7V2IN_PRE_HL7V2_TO_FHIR_MAPPING_PROCESSING(
695                        Boolean.class, "ca.uhn.hl7v2.model.Message", "ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson"),
696
697        /**
698         * <b>HL7v2 Hook:</b>
699         * This hook is invoked after Smile has transformed an HL7V2 message into a collection of Transaction Bundle operations.
700         * <p>
701         * Note that any exceptions thrown by this method will not be trapped by HAPI (they will be passed up to the server)
702         * </p>
703         * <p>
704         * Hooks may accept the following parameters:
705         * <ul>
706         * <li>
707         * {@link ConvertedTransactionBundlesJson} - Contains a list of IBaseBundle objects, each of which represents a transaction about to be executed
708         * against the FHIR repository. Any modifications to this bundle, or additions to it, will be propagated into the repository.
709         * <br/>
710         * <b>NOTE:</b> This parameter is deprecated. Use {@link Hl7v2ToFhirConversionResultJson#getBundles()}
711         * or {@link Hl7v2ToFhirConversionResultJson#setBundles(List)} instead.
712         * </li>
713         * <li>
714         * {@link ca.uhn.hl7v2.model.Message} - the message that was just processed. Any changes to this message will be ignored,
715         * as it has already been processed.
716         * <br/>
717         * <b>NOTE:</b> This parameter is deprecated. Use {@link Hl7v2ToFhirConversionResultJson#getModifiableMessage()}
718         * or {@link Hl7v2ToFhirConversionResultJson#setModifiableMessage(Message)} instead.
719         * </li>
720         * <li>
721         * {@link Hl7v2ToFhirConversionResultJson} - Contains all relevant data involved in the conversion of an HL7 v2.x
722         * message to a list of IBaseBundle resources.
723         * </li>
724         * </ul>
725         * </p>
726         */
727        HL7V2IN_POST_HL7V2_TO_FHIR_MAPPING_PROCESSING(
728                        void.class,
729                        "ca.uhn.hl7v2.model.Message",
730                        "ca.cdr.api.model.json.ConvertedTransactionBundlesJson",
731                        "ca.cdr.api.pub.hl7v2.model.Hl7v2ToFhirConversionResultJson"),
732
733        /**
734         * <b>Persistence (RDBMS) Hook:</b>
735         * This pointcut is invoked my the Persistence (RDBMS) module when
736         * running in MegaScale mode in order to request the database credentials
737         * associated with a given partition ID.
738         * <p>
739         * Hooks may accept the following parameters:
740         * <ul>
741         * <li>
742         * {@link MegaScaleCredentialRequestJson} - Hook methods for this
743         * pointcut should take a {@link MegaScaleCredentialRequestJson}
744         * as input. This object contains the numeric ID of the partition
745         * for which database credentials are wanted.
746         * </li>
747         * </ul>
748         * </p>
749         *
750         * Hook methods for this pointcut must return a
751         * {@link MegaScaleCredentialResponseJson} object which contains the
752         * JDBC URL and credentials associated with the partition ID. These
753         * credentials will be cached, so this Pointcut will not be invoked
754         * repeatedly for the same partition ID (therefore it is ok if hook
755         * methods have some latency).
756         *
757         * @since 2023.02.R01
758         */
759        STORAGE_MEGASCALE_PROVIDE_DB_INFO(MegaScaleCredentialResponseJson.class, MegaScaleCredentialRequestJson.class),
760
761        /**
762         * <b>Server Endpoint Hook:</b>
763         * The pointcut provides the capability to supply a provisioned KeyStore file for TLS base encryption.
764         * Note that pointcut {@link #SERVER_CONFIGURATION_KEYSTORE} is invoked only if the endpoint listener
765         * is said to required TLS encryption for incoming connections through environment property <b>tls.enabled</b>
766         * <p>
767         * <p>
768         * Hooks may accept the following parameters:
769         * <ul>
770         * <li>
771         * {@link java.lang.String} - The keystore password
772         * </li>
773         * </ul>
774         * </p>
775         * <p>
776         * Interceptors for this pointcut must be registered with the specific
777         * endpoint module where the keystore will be used.
778         * </p>
779         * Hook methods must return <code>KeyStore</code>.
780         */
781        SERVER_CONFIGURATION_KEYSTORE(KeyStore.class, String.class),
782
783        /**
784         * <b>SMART/OIDC Hook:</b>
785         * The pointcut is called when a SMART Outbound Security module is configured in
786         * federated mode, and there are multiple federated providers configured, prior to
787         * the user being asked to select a provider. This pointcut can be used to
788         * programmatically select a provider instead of relying on the user to
789         * make a selection.
790         * <p>
791         * Hooks may accept the following parameters:
792         * <ul>
793         * <li>
794         * {@literal ca.cdr.api.fhir.interceptor.OidcAuthRequestDetails} - This object contains details about the auth request and can be used to extract request parameter values.
795         * </li>
796         * </ul>
797         * </p>
798         * Hook methods may return a {@link String}, which should be the the Registration ID of an
799         * OpenID Connect Server definition (i.e. the value of the "Registration ID" field in the
800         * Smile CDR OIDC Server definition page). If the returned String is not {@literal null} and
801         * does not match any server definition, the flow will be halted and the user will
802         * see an error. If the returned string is {@literal null}, the user will be
803         * redirected to a server selection screen.
804         */
805        SMART_FEDERATED_OIDC_PRE_PROVIDER_SELECTION(String.class, OidcAuthRequestDetails.class),
806
807        /**
808         * <b>SMART/OIDC Hook:</b>
809         * The pointcut is called when an OIDC client is being saved. This could
810         * mean that a new client is being created, or an existing client
811         * is being updated or disabled.
812         * <p>
813         * This hook is called after the database transaction used to
814         * save the object has been committed. This means that the record already
815         * appears in the database. Any exceptions thrown by hooks for this
816         * pointcut may cause an error to appear for the user requesting the
817         * operation, but will not affect what has been saved in the database,
818         * so no exceptions should be thrown within this pointcut.
819         * </p>
820         * <p>
821         * Hooks may accept the following parameters:
822         * <ul>
823         * <li>
824         * {@link ca.cdr.api.model.json.IOAuth2ClientDetails} - The Client being saved. Pointcuts should not modify this object.
825         * </li>
826         * </ul>
827         * </p>
828         * Hook methods must return <code>void</code>.
829         */
830        SMART_OIDC_CLIENT_SAVED(void.class, IOAuth2ClientDetails.class),
831
832        /**
833         * <b>SMART/OIDC Hook:</b>
834         * The pointcut is called when an OIDC client is being saved. This could
835         * mean that a new client is being created, or an existing client
836         * is being updated or disabled.
837         * <p>
838         * This hook is called within the open database transaction used to
839         * save the object. This means that at the time this pointcut is invoked,
840         * the record does not yet appear in the database. It also means that any
841         * exception thrown by this pointcut will block the operation.
842         * </p>
843         * <p>
844         * Hooks may accept the following parameters:
845         * <ul>
846         * <li>
847         * {@link ca.cdr.api.model.json.IOAuth2ClientDetails} - The Client being saved. Pointcuts should not modify this object.
848         * </li>
849         * </ul>
850         * </p>
851         * Hook methods must return <code>void</code>.
852         */
853        SMART_OIDC_CLIENT_SAVING(void.class, IOAuth2ClientDetails.class),
854
855        /**
856         * <b>appSphere Hook:</b>
857         * The Pointcut is called when an appSphere admin updates the status of an application or service
858         * <p>
859         * This hook is called within the open database transaction used to
860         * save the object. This means that at the time this pointcut is invoked,
861         * the record does not yet appear in the database. It also means that any
862         * exception thrown by this pointcut will block the operation.
863         * </p>
864         * <p>
865         * Hooks may accept the following parameters:
866         * <ul>
867         * <li>
868         * {@link AGConsoleJson} - The application or service being updated. Pointcuts should not modify this object.
869         * </li>
870         * </ul>
871         * </p>
872         * Hook methods must return <code>void</code>.
873         */
874        AG_APPLICATION_STATUS_UPDATING(void.class, AGConsoleJson.class),
875
876        /**
877         * <b>appSphere Hook:</b>
878         * The pointcut is called after an appSphere admin updates the status of an application or service
879         * <p>
880         * This hook is called after the database transaction used to
881         * save the object has been committed. This means that the record already
882         * appears in the database. Any exceptions thrown by hooks for this
883         * pointcut may cause an error to appear for the user requesting the
884         * operation, but will not affect what has been saved in the database,
885         * so no exceptions should be thrown within this pointcut.
886         * </p>
887         * <p>
888         * Hooks may accept the following parameters:
889         * <ul>
890         * <li>
891         * {@link AGConsoleJson} - The application or service that was updated. Pointcuts should not modify this object.
892         * </li>
893         * </ul>
894         * </p>
895         * Hook methods must return <code>void</code>.
896         */
897        AG_APPLICATION_STATUS_UPDATED(void.class, AGConsoleJson.class),
898
899        /**
900         * <b>System-to-System Data Exchange Hook:</b>
901         * This pointcut is called when doing resource matching in the $member-match operation. It provides the ability for
902         * clients to execute custom matching JavaScript for the patient matching in $member-match.
903         * <p>
904         * Hooks may accept the following parameters:
905         * <ul>
906         * <li>
907         * {@link ca.cdr.api.fhir.interceptor.IMemberMatchRequest} - the wrapper request object that contains memberPatient
908         * and CoverageToMatch Resources.
909         * </li>
910         * <li>
911         * {@link ca.uhn.fhir.rest.api.server.RequestDetails} - A bean containing details about the request that is about to
912         * be processed.
913         * </li>
914         * </ul>
915         * </p>
916         * Hook methods may return <code>Patient</code> if a matching patient is found, or <code>void</code> otherwise.
917         * Note that if <code>void</code> is returned, then the system will perform the default matching algorithm to try to
918         * find any matching patient.
919         */
920        MEMBER_MATCH(IBaseResource.class, IMemberMatchRequest.class, RequestDetails.class);
921
922        private final List<String> myParameterTypes;
923        private final Class<?> myReturnType;
924        private final ExceptionHandlingSpec myExceptionHandlingSpec;
925
926        CdrPointcut(
927                        @Nonnull Class<?> theReturnType,
928                        @Nonnull ExceptionHandlingSpec theExceptionHandlingSpec,
929                        String... theParameterTypes) {
930                myReturnType = theReturnType;
931                myExceptionHandlingSpec = theExceptionHandlingSpec;
932                myParameterTypes = Collections.unmodifiableList(Arrays.asList(theParameterTypes));
933        }
934
935        CdrPointcut(@Nonnull Class<?> theReturnType, String... theParameterTypes) {
936                this(theReturnType, new ExceptionHandlingSpec(), theParameterTypes);
937        }
938
939        CdrPointcut(@Nonnull Class<?> theReturnType, Class<?>... theParameterTypes) {
940                this(theReturnType, new ExceptionHandlingSpec(), toNames(theParameterTypes));
941        }
942
943        CdrPointcut(@Nonnull Class<?> theReturnType) {
944                this(theReturnType, new ExceptionHandlingSpec(), ArrayUtils.EMPTY_STRING_ARRAY);
945        }
946
947        CdrPointcut(@Nonnull Class<?> theReturnType, Class<?> theParameterTypes) {
948                this(theReturnType, new ExceptionHandlingSpec(), theParameterTypes.getName());
949        }
950
951        private static String[] toNames(Class<?>[] theParameterTypes) {
952                return Arrays.stream(theParameterTypes)
953                                .map(t -> t.getName())
954                                .collect(Collectors.toList())
955                                .toArray(new String[0]);
956        }
957
958        private static Class<?> toReturnTypeClass(String theReturnType) {
959                try {
960                        return Class.forName(theReturnType);
961                } catch (ClassNotFoundException theE) {
962                        return UnknownType.class;
963                }
964        }
965
966        @Override
967        public boolean isShouldLogAndSwallowException(@Nonnull Throwable theException) {
968                for (Class<? extends Throwable> next : myExceptionHandlingSpec.myTypesToLogAndSwallow) {
969                        if (next.isAssignableFrom(theException.getClass())) {
970                                return true;
971                        }
972                }
973                return false;
974        }
975
976        @Override
977        @Nonnull
978        public Class<?> getReturnType() {
979                return myReturnType;
980        }
981
982        @Override
983        @Nonnull
984        public List<String> getParameterTypes() {
985                return myParameterTypes;
986        }
987
988        private static class UnknownType {}
989
990        private static class ExceptionHandlingSpec {
991
992                private final Set<Class<? extends Throwable>> myTypesToLogAndSwallow = new HashSet<>();
993
994                ExceptionHandlingSpec addLogAndSwallow(@Nonnull Class<? extends Throwable> theType) {
995                        myTypesToLogAndSwallow.add(theType);
996                        return this;
997                }
998        }
999}