@@ -14,6 +14,7 @@ APIs to their New Platform equivalents.
1414 - [ 3. New Platform shim using New Platform router] ( #3-new-platform-shim-using-new-platform-router )
1515 - [ 4. New Platform plugin] ( #4-new-platform-plugin )
1616 - [ Accessing Services] ( #accessing-services )
17+ - [ Migrating Hapi "pre" handlers] ( #migrating-hapi-pre-handlers )
1718 - [ Chrome] ( #chrome )
1819 - [ Updating an application navlink] ( #updating-application-navlink )
1920 - [ Chromeless Applications] ( #chromeless-applications )
@@ -450,6 +451,142 @@ class Plugin {
450451}
451452` ` `
452453
454+ ### Migrating Hapi "pre" handlers
455+
456+ In the Legacy Platform, routes could provide a "pre" option in their config to
457+ register a function that should be run prior to the route handler. These
458+ "pre" handlers allow routes to share some business logic that may do some
459+ pre-work or validation. In Kibana, these are often used for license checks.
460+
461+ The Kibana Platform's HTTP interface does not provide this functionality,
462+ however it is simple enough to port over using a higher-order function that can
463+ wrap the route handler.
464+
465+ #### Simple example
466+
467+ In this simple example, a pre-handler is used to either abort the request with
468+ an error or continue as normal. This is a simple "gate-keeping" pattern.
469+
470+ ` ` ` ts
471+ // Legacy pre-handler
472+ const licensePreRouting = (request ) => {
473+ const licenseInfo = getMyPluginLicenseInfo (request .server .plugins .xpack_main );
474+ if (!licenseInfo.isOneOf([' gold' , ' platinum' , ' trial' ])) {
475+ throw Boom .forbidden (` You don't have the right license for MyPlugin! ` );
476+ }
477+ }
478+
479+ server .route ({
480+ method: ' GET' ,
481+ path: ' /api/my-plugin/do-something' ,
482+ config: {
483+ pre: [{ method: licensePreRouting }]
484+ },
485+ handler: (req ) => {
486+ return doSomethingInteresting();
487+ }
488+ })
489+ ` ` `
490+
491+ In the Kibana Platform, the same functionality can be acheived by creating a
492+ function that takes a route handler (or factory for a route handler) as an
493+ argument and either invokes it in the successful case or returns an error
494+ response in the failure case.
495+
496+ We'll call this a "high-order handler" similar to the "high-order component"
497+ pattern common in the React ecosystem.
498+
499+ ` ` ` ts
500+ // New Platform high-order handler
501+ const checkLicense = <P , Q , B >(
502+ handler : RequestHandler <P , Q , B , RouteMethod >
503+ ): RequestHandler <P , Q , B , RouteMethod > => {
504+ return (context , req , res ) => {
505+ const licenseInfo = getMyPluginLicenseInfo (context .licensing .license );
506+
507+ if (licenseInfo.hasAtLeast(' gold' )) {
508+ return handler (context , req , res );
509+ } else {
510+ return res .forbidden ({ body: ` You don't have the right license for MyPlugin! ` });
511+ }
512+ }
513+ }
514+
515+ router .get (
516+ { path: ' /api/my-plugin/do-something' , validate: false },
517+ checkLicense (async (context , req , res ) => {
518+ const results = doSomethingInteresting ();
519+ return res .ok ({ body: results });
520+ }),
521+ )
522+ ` ` `
523+
524+ #### Full Example
525+
526+ In some cases, the route handler may need access to data that the pre-handler
527+ retrieves. In this case, you can utilize a handler _factory_ rather than a raw
528+ handler.
529+
530+ ` ` ` ts
531+ // Legacy pre-handler
532+ const licensePreRouting = (request ) => {
533+ const licenseInfo = getMyPluginLicenseInfo (request .server .plugins .xpack_main );
534+ if (licenseInfo.isOneOf([' gold' , ' platinum' , ' trial' ])) {
535+ // In this case, the return value of the pre-handler is made available on
536+ // whatever the 'assign' option is in the route config.
537+ return licenseInfo ;
538+ } else {
539+ // In this case, the route handler is never called and the user gets this
540+ // error message
541+ throw Boom .forbidden (` You don't have the right license for MyPlugin! ` );
542+ }
543+ }
544+
545+ server .route ({
546+ method: ' GET' ,
547+ path: ' /api/my-plugin/do-something' ,
548+ config: {
549+ pre: [{ method: licensePreRouting , assign: ' licenseInfo' }]
550+ },
551+ handler: (req ) => {
552+ const licenseInfo = req .pre .licenseInfo ;
553+ return doSomethingInteresting(licenseInfo );
554+ }
555+ })
556+ ` ` `
557+
558+ In many cases, it may be simpler to duplicate the function call
559+ to retrieve the data again in the main handler. In this other cases, you can
560+ utilize a handler _factory_ rather than a raw handler as the argument to your
561+ high-order handler. This way the high-order handler can pass arbitrary arguments
562+ to the route handler.
563+
564+ ` ` ` ts
565+ // New Platform high-order handler
566+ const checkLicense = <P , Q , B >(
567+ handlerFactory : (licenseInfo : MyPluginLicenseInfo ) => RequestHandler <P , Q , B , RouteMethod >
568+ ): RequestHandler <P , Q , B , RouteMethod > => {
569+ return (context , req , res ) => {
570+ const licenseInfo = getMyPluginLicenseInfo (context .licensing .license );
571+
572+ if (licenseInfo.hasAtLeast(' gold' )) {
573+ const handler = handlerFactory (licenseInfo );
574+ return handler (context , req , res );
575+ } else {
576+ return res .forbidden ({ body: ` You don't have the right license for MyPlugin! ` });
577+ }
578+ }
579+ }
580+
581+ router .get (
582+ { path: ' /api/my-plugin/do-something' , validate: false },
583+ checkLicense (licenseInfo = > async (context , req , res ) => {
584+ const results = doSomethingInteresting (licenseInfo );
585+ return res .ok ({ body: results });
586+ }),
587+ )
588+ ` ` `
589+
453590## Chrome
454591
455592In the Legacy Platform, the ` ui /chrome ` import contained APIs for a very wide
0 commit comments