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