diff --git a/src/plugins/workspace/common/constants.ts b/src/plugins/workspace/common/constants.ts index 5bd8ab34c313..25deceac0c0f 100644 --- a/src/plugins/workspace/common/constants.ts +++ b/src/plugins/workspace/common/constants.ts @@ -18,3 +18,5 @@ export enum WorkspacePermissionMode { LibraryRead = 'library_read', LibraryWrite = 'library_write', } + +export const WORKSPACE_ID_CONSUMER_WRAPPER_ID = 'workspace_id_consumer'; diff --git a/src/plugins/workspace/opensearch_dashboards.json b/src/plugins/workspace/opensearch_dashboards.json index 7d94a7491a00..0f1d851d5d76 100644 --- a/src/plugins/workspace/opensearch_dashboards.json +++ b/src/plugins/workspace/opensearch_dashboards.json @@ -3,9 +3,7 @@ "version": "opensearchDashboards", "server": true, "ui": true, - "requiredPlugins": [ - "savedObjects" - ], + "requiredPlugins": [], "optionalPlugins": ["savedObjectsManagement"], "requiredBundles": ["opensearchDashboardsReact"] } diff --git a/src/plugins/workspace/server/constant.ts b/src/plugins/workspace/server/constant.ts new file mode 100644 index 000000000000..d3cd904b1cf5 --- /dev/null +++ b/src/plugins/workspace/server/constant.ts @@ -0,0 +1,11 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * A symbol used for imply the workspace id in original url + * + * @Internal + */ +export const workspaceIdInUrlSymbol = Symbol('workspace_id_in_url'); diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index d425f17d08eb..3121c0c39846 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -15,6 +15,7 @@ import { import { WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID, WORKSPACE_CONFLICT_CONTROL_SAVED_OBJECTS_CLIENT_WRAPPER_ID, + WORKSPACE_ID_CONSUMER_WRAPPER_ID, } from '../common/constants'; import { IWorkspaceClientImpl } from './types'; import { WorkspaceClient } from './workspace_client'; @@ -27,6 +28,10 @@ import { SavedObjectsPermissionControlContract, } from './permission_control/client'; import { WorkspacePluginConfigType } from '../config'; +import { workspaceIdInUrlSymbol } from './constant'; +import { WorkspaceIdConsumerWrapper } from './saved_objects/workspace_id_consumer_wrapper'; +// eslint-disable-next-line @osd/eslint/no-restricted-paths +import { ensureRawRequest } from '../../../core/server/http/router'; // will be an issue as the ensureRawRequest is an internal implementation export class WorkspacePlugin implements Plugin<{}, {}> { private readonly logger: Logger; @@ -44,6 +49,8 @@ export class WorkspacePlugin implements Plugin<{}, {}> { const workspaceId = getWorkspaceIdFromUrl(request.url.toString()); if (workspaceId) { + const rawRequest = ensureRawRequest(request); + rawRequest.headers[workspaceIdInUrlSymbol.toString()] = workspaceId; const requestUrl = new URL(request.url.toString()); requestUrl.pathname = cleanWorkspaceId(requestUrl.pathname); return toolkit.rewriteUrl(requestUrl.toString()); @@ -91,6 +98,12 @@ export class WorkspacePlugin implements Plugin<{}, {}> { ); } + core.savedObjects.addClientWrapper( + -2, + WORKSPACE_ID_CONSUMER_WRAPPER_ID, + new WorkspaceIdConsumerWrapper().wrapperFactory + ); + registerRoutes({ http: core.http, logger: this.logger, diff --git a/src/plugins/workspace/server/saved_objects/workspace_id_consumer_wrapper.ts b/src/plugins/workspace/server/saved_objects/workspace_id_consumer_wrapper.ts new file mode 100644 index 000000000000..4c302fc93518 --- /dev/null +++ b/src/plugins/workspace/server/saved_objects/workspace_id_consumer_wrapper.ts @@ -0,0 +1,117 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + SavedObjectsBaseOptions, + SavedObjectsBulkCreateObject, + SavedObjectsClientWrapperFactory, + SavedObjectsCreateOptions, + SavedObjectsCheckConflictsObject, + OpenSearchDashboardsRequest, + SavedObjectsFindOptions, + SavedObjectsUpdateOptions, + SavedObjectsBulkUpdateOptions, + SavedObjectsBulkUpdateObject, + WORKSPACE_TYPE, +} from '../../../../core/server'; +import { workspaceIdInUrlSymbol } from '../constant'; + +type WorkspaceOptions = + | { + workspaces?: string[]; + } + | undefined; + +export class WorkspaceIdConsumerWrapper { + private typeRelatedToWorkspace(type: string | string[]): boolean { + if (Array.isArray(type)) { + return type.some((item) => item === WORKSPACE_TYPE); + } + + return type === WORKSPACE_TYPE; + } + private formatWorkspaceIdParams( + request: OpenSearchDashboardsRequest, + options: T + ): T { + if (!options) { + return options; + } + const { workspaces, ...others } = options; + const workspaceIdParsedFromUrl = request.headers[workspaceIdInUrlSymbol.toString()] as string; + const workspaceIdInUserOptions = options.workspaces; + let finalWorkspaces: string[] = []; + if (workspaceIdInUserOptions?.length) { + finalWorkspaces = workspaceIdInUserOptions; + } else if (workspaceIdParsedFromUrl) { + finalWorkspaces = [workspaceIdParsedFromUrl]; + } + + return { + ...(others as T), + ...(finalWorkspaces.length ? { workspaces: finalWorkspaces } : {}), + }; + } + public wrapperFactory: SavedObjectsClientWrapperFactory = (wrapperOptions) => { + return { + ...wrapperOptions.client, + create: (type: string, attributes: T, options: SavedObjectsCreateOptions = {}) => + wrapperOptions.client.create( + type, + attributes, + this.formatWorkspaceIdParams(wrapperOptions.request, options) + ), + bulkCreate: ( + objects: Array>, + options: SavedObjectsCreateOptions = {} + ) => + wrapperOptions.client.bulkCreate( + objects, + this.formatWorkspaceIdParams(wrapperOptions.request, options) + ), + checkConflicts: ( + objects: SavedObjectsCheckConflictsObject[] = [], + options: SavedObjectsBaseOptions = {} + ) => + wrapperOptions.client.checkConflicts( + objects, + this.formatWorkspaceIdParams(wrapperOptions.request, options) + ), + delete: wrapperOptions.client.delete, + find: (options: SavedObjectsFindOptions) => + wrapperOptions.client.find( + this.typeRelatedToWorkspace(options.type) + ? options + : this.formatWorkspaceIdParams(wrapperOptions.request, options) + ), + bulkGet: wrapperOptions.client.bulkGet, + get: wrapperOptions.client.get, + update: ( + type: string, + id: string, + attributes: Partial, + options: SavedObjectsUpdateOptions = {} + ) => + wrapperOptions.client.update( + type, + id, + attributes, + this.formatWorkspaceIdParams(wrapperOptions.request, options) + ), + bulkUpdate: ( + objects: Array>, + options?: SavedObjectsBulkUpdateOptions + ) => + wrapperOptions.client.bulkUpdate( + objects, + this.formatWorkspaceIdParams(wrapperOptions.request, options) + ), + addToNamespaces: wrapperOptions.client.addToNamespaces, + deleteFromNamespaces: wrapperOptions.client.deleteFromNamespaces, + }; + }; + + constructor() {} +} diff --git a/src/plugins/workspace/server/workspace_client.ts b/src/plugins/workspace/server/workspace_client.ts index e7bdf97b54ec..0ffb72f016f2 100644 --- a/src/plugins/workspace/server/workspace_client.ts +++ b/src/plugins/workspace/server/workspace_client.ts @@ -21,6 +21,7 @@ import { IWorkspaceClientImpl, WorkspaceFindOptions, IResponse, IRequestDetail } import { workspace } from './saved_objects'; import { generateRandomId } from './utils'; import { + WORKSPACE_ID_CONSUMER_WRAPPER_ID, WORKSPACE_OVERVIEW_APP_ID, WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID, WORKSPACE_UPDATE_APP_ID,