diff --git a/common/constants.ts b/common/constants.ts index 86bacf99..b569665d 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -6,6 +6,13 @@ export const PLUGIN_ID = 'flow-framework'; export const BASE_NODE_API_PATH = '/api/flow_framework'; + +// OpenSearch APIs export const BASE_INDICES_NODE_API_PATH = `${BASE_NODE_API_PATH}/indices`; export const SEARCH_INDICES_PATH = `${BASE_INDICES_NODE_API_PATH}/search`; export const FETCH_INDICES_PATH = `${BASE_INDICES_NODE_API_PATH}/fetch`; + +// Flow Framework APIs +export const BASE_WORKFLOW_NODE_API_PATH = `${BASE_NODE_API_PATH}/workflow`; +export const GET_WORKFLOW_PATH = `${BASE_WORKFLOW_NODE_API_PATH}`; +export const CREATE_WORKFLOW_PATH = `${BASE_WORKFLOW_NODE_API_PATH}/create`; diff --git a/public/route_service.ts b/public/route_service.ts index 54b2a76e..82e04363 100644 --- a/public/route_service.ts +++ b/public/route_service.ts @@ -15,7 +15,7 @@ export function configureRoutes(core: CoreStart): RouteService { return { searchIndex: async (indexName: string, body: {}) => { try { - const response = await core.http.post<{ respString: string }>( + const response = await core.http.get<{ respString: string }>( `${SEARCH_INDICES_PATH}/${indexName}`, { body: JSON.stringify(body), @@ -28,7 +28,7 @@ export function configureRoutes(core: CoreStart): RouteService { }, fetchIndices: async (pattern: string) => { try { - const response = await core.http.post<{ respString: string }>( + const response = await core.http.get<{ respString: string }>( `${FETCH_INDICES_PATH}/${pattern}` ); return response; diff --git a/server/cluster/flow_framework_plugin.ts b/server/cluster/flow_framework_plugin.ts new file mode 100644 index 00000000..74e39bcc --- /dev/null +++ b/server/cluster/flow_framework_plugin.ts @@ -0,0 +1,26 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX } from '../utils'; + +export function flowFrameworkPlugin(Client: any, config: any, components: any) { + const ca = components.clientAction.factory; + + Client.prototype.flowFramework = components.clientAction.namespaceFactory(); + const flowFramework = Client.prototype.flowFramework.prototype; + + flowFramework.deleteDetector = ca({ + url: { + fmt: `${FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX}/<%=workflow_id%>`, + req: { + workflow_id: { + type: 'string', + required: true, + }, + }, + }, + method: 'GET', + }); +} diff --git a/server/cluster/index.ts b/server/cluster/index.ts new file mode 100644 index 00000000..5ff70614 --- /dev/null +++ b/server/cluster/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './flow_framework_plugin'; diff --git a/server/plugin.ts b/server/plugin.ts index 2778db98..5c253614 100644 --- a/server/plugin.ts +++ b/server/plugin.ts @@ -10,12 +10,20 @@ import { Plugin, Logger, } from '../../../src/core/server'; - +import { first } from 'rxjs/operators'; +import { flowFrameworkPlugin } from './cluster'; import { FlowFrameworkDashboardsPluginSetup, FlowFrameworkDashboardsPluginStart, } from './types'; -import { registerOpenSearchRoutes } from './routes'; +import { + registerOpenSearchRoutes, + registerFlowFrameworkRoutes, + OpenSearchRoutesService, + FlowFrameworkRoutesService, +} from './routes'; + +import { ILegacyClusterClient } from '../../../src/core/server/'; export class FlowFrameworkDashboardsPlugin implements @@ -24,17 +32,35 @@ export class FlowFrameworkDashboardsPlugin FlowFrameworkDashboardsPluginStart > { private readonly logger: Logger; + private readonly globalConfig$: any; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); + this.globalConfig$ = initializerContext.config.legacy.globalConfig$; } - public setup(core: CoreSetup) { + public async setup(core: CoreSetup) { this.logger.debug('flow-framework-dashboards: Setup'); const router = core.http.createRouter(); - // Register server side APIs - registerOpenSearchRoutes(router); + // Get any custom/overridden headers + const globalConfig = await this.globalConfig$.pipe(first()).toPromise(); + + // Create OpenSearch client w/ relevant plugins and headers + const client: ILegacyClusterClient = core.opensearch.legacy.createClient( + 'flow_framework', + { + plugins: [flowFrameworkPlugin], + ...globalConfig.opensearch, + } + ); + + const opensearchRoutesService = new OpenSearchRoutesService(client); + const flowFrameworkRoutesService = new FlowFrameworkRoutesService(client); + + // Register server side APIs with the corresponding service functions + registerOpenSearchRoutes(router, opensearchRoutesService); + registerFlowFrameworkRoutes(router, flowFrameworkRoutesService); return {}; } diff --git a/server/routes/flow_framework_routes_service.ts b/server/routes/flow_framework_routes_service.ts new file mode 100644 index 00000000..a4bbb746 --- /dev/null +++ b/server/routes/flow_framework_routes_service.ts @@ -0,0 +1,60 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { schema } from '@osd/config-schema'; +import { + IRouter, + IOpenSearchDashboardsResponse, + RequestHandlerContext, + OpenSearchDashboardsRequest, + OpenSearchDashboardsResponseFactory, +} from '../../../../src/core/server'; +import { GET_WORKFLOW_PATH } from '../../common'; +import { generateCustomError } from './helpers'; + +export function registerFlowFrameworkRoutes( + router: IRouter, + flowFrameworkRoutesService: FlowFrameworkRoutesService +): void { + router.post( + { + path: `${GET_WORKFLOW_PATH}/{workflow_id}`, + validate: { + params: schema.object({ + index_name: schema.string(), + }), + body: schema.any(), + }, + }, + flowFrameworkRoutesService.getWorkflow + ); +} + +export class FlowFrameworkRoutesService { + private client: any; + + constructor(client: any) { + this.client = client; + } + + getWorkflow = async ( + context: RequestHandlerContext, + req: OpenSearchDashboardsRequest, + res: OpenSearchDashboardsResponseFactory + ): Promise> => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { workflow_id } = req.params; + + try { + const response = await this.client + .asScoped(req) + .callAsCurrentUser('flowFramework.getWorkflow', { workflow_id }); + console.log('response from get workflow: ', response); + return res.ok({ body: response }); + } catch (err: any) { + return generateCustomError(res, err); + } + }; +} diff --git a/server/routes/index.ts b/server/routes/index.ts index 00b49143..8b55b8cb 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -3,4 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -export * from './opensearch_routes'; +export * from './opensearch_routes_service'; +export * from './flow_framework_routes_service'; diff --git a/server/routes/opensearch_routes.ts b/server/routes/opensearch_routes.ts deleted file mode 100644 index 5454c4b7..00000000 --- a/server/routes/opensearch_routes.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { schema } from '@osd/config-schema'; -import { SearchRequest } from '@opensearch-project/opensearch/api/types'; -import { - IRouter, - IOpenSearchDashboardsResponse, -} from '../../../../src/core/server'; -import { SEARCH_INDICES_PATH, FETCH_INDICES_PATH, Index } from '../../common'; -import { generateCustomError } from './helpers'; - -export function registerOpenSearchRoutes(router: IRouter): void { - router.post( - { - path: `${SEARCH_INDICES_PATH}/{index_name}`, - validate: { - params: schema.object({ - index_name: schema.string(), - }), - body: schema.any(), - }, - }, - async (context, req, res): Promise> => { - const client = context.core.opensearch.client.asCurrentUser; - // eslint-disable-next-line @typescript-eslint/naming-convention - const { index_name } = req.params; - const body = req.body; - - const params = { - index: index_name, - body, - } as SearchRequest; - - try { - const response = await client.search(params); - return res.ok({ body: response }); - } catch (err: any) { - return generateCustomError(res, err); - } - } - ); - router.post( - { - path: `${FETCH_INDICES_PATH}/{pattern}`, - validate: { - params: schema.object({ - pattern: schema.string(), - }), - }, - }, - async (context, req, res): Promise> => { - const client = context.core.opensearch.client.asCurrentUser; - const { pattern } = req.params; - try { - const response = await client.cat.indices({ - index: pattern, - format: 'json', - h: 'health,index', - }); - - // re-formatting the index results to match Index - const cleanedIndices = response.body.map((index) => ({ - name: index.index, - health: index.health, - })) as Index[]; - - return res.ok({ body: cleanedIndices }); - } catch (err: any) { - return generateCustomError(res, err); - } - } - ); -} diff --git a/server/routes/opensearch_routes_service.ts b/server/routes/opensearch_routes_service.ts new file mode 100644 index 00000000..88accf20 --- /dev/null +++ b/server/routes/opensearch_routes_service.ts @@ -0,0 +1,105 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { schema } from '@osd/config-schema'; +import { SearchRequest } from '@opensearch-project/opensearch/api/types'; +import { + IRouter, + IOpenSearchDashboardsResponse, + RequestHandlerContext, + OpenSearchDashboardsRequest, + OpenSearchDashboardsResponseFactory, +} from '../../../../src/core/server'; +import { SEARCH_INDICES_PATH, FETCH_INDICES_PATH, Index } from '../../common'; +import { generateCustomError } from './helpers'; + +export function registerOpenSearchRoutes( + router: IRouter, + opensearchRoutesService: OpenSearchRoutesService +): void { + router.post( + { + path: `${SEARCH_INDICES_PATH}/{index_name}`, + validate: { + params: schema.object({ + index_name: schema.string(), + }), + body: schema.any(), + }, + }, + opensearchRoutesService.searchIndex + ); + + router.post( + { + path: `${FETCH_INDICES_PATH}/{pattern}`, + validate: { + params: schema.object({ + pattern: schema.string(), + }), + }, + }, + opensearchRoutesService.catIndices + ); +} + +export class OpenSearchRoutesService { + private client: any; + + constructor(client: any) { + this.client = client; + } + + searchIndex = async ( + context: RequestHandlerContext, + req: OpenSearchDashboardsRequest, + res: OpenSearchDashboardsResponseFactory + ): Promise> => { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { index_name } = req.params; + const body = req.body; + + const params = { + index: index_name, + body, + } as SearchRequest; + + try { + const response = await this.client + .asScoped(req) + .callAsCurrentUser.search(params); + return res.ok({ body: response }); + } catch (err: any) { + return generateCustomError(res, err); + } + }; + + catIndices = async ( + context: RequestHandlerContext, + req: OpenSearchDashboardsRequest, + res: OpenSearchDashboardsResponseFactory + ): Promise> => { + const { pattern } = req.params; + try { + const response = await this.client + .asScoped(req) + .callAsCurrentUser.cat.indices({ + index: pattern, + format: 'json', + h: 'health,index', + }); + + // re-formatting the index results to match Index + const cleanedIndices = response.body.map((index) => ({ + name: index.index, + health: index.health, + })) as Index[]; + + return res.ok({ body: cleanedIndices }); + } catch (err: any) { + return generateCustomError(res, err); + } + }; +} diff --git a/server/utils/constants.ts b/server/utils/constants.ts new file mode 100644 index 00000000..de153ce1 --- /dev/null +++ b/server/utils/constants.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const FLOW_FRAMEWORK_API_ROUTE_PREFIX = '/_plugins/_flow_framework'; +export const FLOW_FRAMEWORK_WORKFLOW_ROUTE_PREFIX = `${FLOW_FRAMEWORK_API_ROUTE_PREFIX}/workflow`; diff --git a/server/utils/index.ts b/server/utils/index.ts new file mode 100644 index 00000000..2e209c79 --- /dev/null +++ b/server/utils/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './constants';