From f0e1cc112fe0f337a750ce8e875dba3227c2c3c6 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Mon, 4 Nov 2024 11:56:49 -0500 Subject: [PATCH] Put experimental HTTP API routes behind feature flag To enable HTTP API routes add to kibana.yml ``` feature_flags.overrides: dashboard.enableExperimentalHttpApi: true ``` --- src/plugins/dashboard/server/api/constants.ts | 2 + .../dashboard/server/api/register_routes.ts | 54 +++++++++++++++---- src/plugins/dashboard/server/feature_flags.ts | 33 ++++++++++++ test/api_integration/config.js | 4 ++ 4 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/plugins/dashboard/server/feature_flags.ts diff --git a/src/plugins/dashboard/server/api/constants.ts b/src/plugins/dashboard/server/api/constants.ts index 458165d79786..de1297e45986 100644 --- a/src/plugins/dashboard/server/api/constants.ts +++ b/src/plugins/dashboard/server/api/constants.ts @@ -10,3 +10,5 @@ export const PUBLIC_API_VERSION = '2023-10-31'; export const PUBLIC_API_CONTENT_MANAGEMENT_VERSION = 3; export const PUBLIC_API_PATH = '/api/dashboards/dashboard'; + +export const PUBLIC_API_FEATURE_FLAG = 'dashboard.enableExperimentalHttpApi'; diff --git a/src/plugins/dashboard/server/api/register_routes.ts b/src/plugins/dashboard/server/api/register_routes.ts index 692942e1bd1b..df23009f9981 100644 --- a/src/plugins/dashboard/server/api/register_routes.ts +++ b/src/plugins/dashboard/server/api/register_routes.ts @@ -9,7 +9,12 @@ import { schema } from '@kbn/config-schema'; import type { ContentManagementServerSetup } from '@kbn/content-management-plugin/server'; -import type { HttpServiceSetup } from '@kbn/core/server'; +import type { + HttpServiceSetup, + RequestHandler, + RequestHandlerContext, + RouteMethod, +} from '@kbn/core/server'; import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; import type { Logger } from '@kbn/logging'; @@ -18,6 +23,7 @@ import { PUBLIC_API_PATH, PUBLIC_API_VERSION, PUBLIC_API_CONTENT_MANAGEMENT_VERSION, + PUBLIC_API_FEATURE_FLAG, } from './constants'; import { dashboardAttributesSchema, @@ -37,6 +43,32 @@ interface RegisterAPIRoutesArgs { const TECHNICAL_PREVIEW_WARNING = 'This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.'; +const createFeatureFlagRouteHandler = < + P, + Q, + B, + Context extends RequestHandlerContext, + Method extends RouteMethod +>( + handler: RequestHandler +) => { + const featureFlagRouteHandler: RequestHandler = async ( + ctx, + req, + res + ) => { + const { featureFlags } = await ctx.core; + const isFeatureFlagEnabled = await featureFlags.getBooleanValue(PUBLIC_API_FEATURE_FLAG, false); + if (!isFeatureFlagEnabled) { + return res.badRequest({ + body: `uri [${req.url.pathname}] with method [${req.route.method}] exists but is not available with the current configuration`, + }); + } + return handler(ctx, req, res); + }; + return featureFlagRouteHandler; +}; + export function registerAPIRoutes({ http, contentManagement, @@ -77,7 +109,7 @@ export function registerAPIRoutes({ }, }, }, - async (ctx, req, res) => { + createFeatureFlagRouteHandler(async (ctx, req, res) => { const { id } = req.params; const { attributes, references, spaces: initialNamespaces } = req.body; const client = contentManagement.contentClient @@ -107,7 +139,7 @@ export function registerAPIRoutes({ } return res.ok({ body: result }); - } + }) ); // Update API route @@ -142,7 +174,7 @@ export function registerAPIRoutes({ }, }, }, - async (ctx, req, res) => { + createFeatureFlagRouteHandler(async (ctx, req, res) => { const { attributes, references } = req.body; const client = contentManagement.contentClient .getForRequest({ request: req, requestHandlerContext: ctx }) @@ -165,7 +197,7 @@ export function registerAPIRoutes({ } return res.created({ body: result }); - } + }) ); // List API route @@ -200,7 +232,7 @@ export function registerAPIRoutes({ }, }, }, - async (ctx, req, res) => { + createFeatureFlagRouteHandler(async (ctx, req, res) => { const { page, perPage: limit } = req.query; const client = contentManagement.contentClient .getForRequest({ request: req, requestHandlerContext: ctx }) @@ -222,7 +254,7 @@ export function registerAPIRoutes({ total: result.pagination.total, }; return res.ok({ body }); - } + }) ); // Get API route @@ -252,7 +284,7 @@ export function registerAPIRoutes({ }, }, }, - async (ctx, req, res) => { + createFeatureFlagRouteHandler(async (ctx, req, res) => { const client = contentManagement.contentClient .getForRequest({ request: req, requestHandlerContext: ctx }) .for(CONTENT_ID, PUBLIC_API_CONTENT_MANAGEMENT_VERSION); @@ -276,7 +308,7 @@ export function registerAPIRoutes({ } return res.ok({ body: result }); - } + }) ); // Delete API route @@ -301,7 +333,7 @@ export function registerAPIRoutes({ }, }, }, - async (ctx, req, res) => { + createFeatureFlagRouteHandler(async (ctx, req, res) => { const client = contentManagement.contentClient .getForRequest({ request: req, requestHandlerContext: ctx }) .for(CONTENT_ID, PUBLIC_API_CONTENT_MANAGEMENT_VERSION); @@ -322,6 +354,6 @@ export function registerAPIRoutes({ } return res.ok(); - } + }) ); } diff --git a/src/plugins/dashboard/server/feature_flags.ts b/src/plugins/dashboard/server/feature_flags.ts new file mode 100644 index 000000000000..bf43f58d68db --- /dev/null +++ b/src/plugins/dashboard/server/feature_flags.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { FeatureFlagDefinitions } from '@kbn/core-feature-flags-server'; +import { PUBLIC_API_FEATURE_FLAG } from './api/constants'; + +export const featureFlags: FeatureFlagDefinitions = [ + { + key: PUBLIC_API_FEATURE_FLAG, + name: 'Dashboard HTTP API', + description: 'Enable or disable the Dashboard HTTP API endpoints', + tags: ['dashboard'], + variationType: 'boolean', + variations: [ + { + name: 'Enabled', + description: 'Enables the Dashboard HTTP API endpoints', + value: true, + }, + { + name: 'Disabled', + description: 'Disables the Dashboard HTTP API endpoints', + value: false, + }, + ], + }, +]; diff --git a/test/api_integration/config.js b/test/api_integration/config.js index ec64e6fa03f3..cfa6e931fce6 100644 --- a/test/api_integration/config.js +++ b/test/api_integration/config.js @@ -37,6 +37,10 @@ export default async function ({ readConfigFile }) { '--savedObjects.maxImportPayloadBytes=30000000', // for testing set buffer duration to 0 to immediately flush counters into saved objects. '--usageCollection.usageCounters.bufferDuration=0', + // enable dashboard API endpoints for testing + `--feature_flags.overrides=${JSON.stringify({ + 'dashboard.enableExperimentalHttpApi': true, + })}`, ], }, };