/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ import { Observable, BehaviorSubject } from 'rxjs'; import { skipWhile } from 'rxjs/operators'; import { HttpSetup } from 'src/core/public'; import { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; import { Space } from '../../common/model/space'; import { GetAllSpacesPurpose, GetSpaceResult } from '../../common/model/types'; import { CopySavedObjectsToSpaceResponse } from '../copy_saved_objects_to_space/types'; type SavedObject = Pick; interface GetAllSpacesOptions { purpose?: GetAllSpacesPurpose; includeAuthorizedPurposes?: boolean; } export class SpacesManager { private activeSpace$: BehaviorSubject = new BehaviorSubject(null); private readonly serverBasePath: string; public readonly onActiveSpaceChange$: Observable; constructor(private readonly http: HttpSetup) { this.serverBasePath = http.basePath.serverBasePath; this.onActiveSpaceChange$ = this.activeSpace$ .asObservable() .pipe(skipWhile((v: Space | null) => v == null)) as Observable; this.refreshActiveSpace(); } public async getSpaces(options: GetAllSpacesOptions = {}): Promise { const { purpose, includeAuthorizedPurposes } = options; const query = { purpose, include_authorized_purposes: includeAuthorizedPurposes }; return await this.http.get('/api/spaces/space', { query }); } public async getSpace(id: string): Promise { return await this.http.get(`/api/spaces/space/${encodeURIComponent(id)}`); } public getActiveSpace({ forceRefresh = false } = {}) { if (this.isAnonymousPath()) { throw new Error(`Cannot retrieve the active space for anonymous paths`); } if (!forceRefresh && this.activeSpace$.value) { return Promise.resolve(this.activeSpace$.value); } return this.http.get('/internal/spaces/_active_space') as Promise; } public async createSpace(space: Space) { await this.http.post(`/api/spaces/space`, { body: JSON.stringify(space), }); } public async updateSpace(space: Space) { await this.http.put(`/api/spaces/space/${encodeURIComponent(space.id)}`, { query: { overwrite: true, }, body: JSON.stringify(space), }); const activeSpaceId = (await this.getActiveSpace()).id; if (space.id === activeSpaceId) { this.refreshActiveSpace(); } } public async deleteSpace(space: Space) { await this.http.delete(`/api/spaces/space/${encodeURIComponent(space.id)}`); } public async copySavedObjects( objects: SavedObject[], spaces: string[], includeReferences: boolean, createNewCopies: boolean, overwrite: boolean ): Promise { return this.http.post('/api/spaces/_copy_saved_objects', { body: JSON.stringify({ objects, spaces, includeReferences, ...(createNewCopies ? { createNewCopies } : { overwrite }), }), }); } public async resolveCopySavedObjectsErrors( objects: SavedObject[], retries: unknown, includeReferences: boolean, createNewCopies: boolean ): Promise { return this.http.post(`/api/spaces/_resolve_copy_saved_objects_errors`, { body: JSON.stringify({ objects, includeReferences, createNewCopies, retries, }), }); } public async getShareSavedObjectPermissions( type: string ): Promise<{ shareToAllSpaces: boolean }> { return this.http .get('/internal/security/_share_saved_object_permissions', { query: { type } }) .catch((err) => { const isNotFound = err?.body?.statusCode === 404; if (isNotFound) { // security is not enabled return { shareToAllSpaces: true }; } throw err; }); } public async shareSavedObjectAdd(object: SavedObject, spaces: string[]): Promise { return this.http.post(`/api/spaces/_share_saved_object_add`, { body: JSON.stringify({ object, spaces }), }); } public async shareSavedObjectRemove(object: SavedObject, spaces: string[]): Promise { return this.http.post(`/api/spaces/_share_saved_object_remove`, { body: JSON.stringify({ object, spaces }), }); } public redirectToSpaceSelector() { window.location.href = `${this.serverBasePath}/spaces/space_selector`; } private async refreshActiveSpace() { // Anonymous paths (such as login/logout) should not request the active space under any circumstances. if (this.isAnonymousPath()) { return; } const activeSpace = await this.getActiveSpace({ forceRefresh: true }); this.activeSpace$.next(activeSpace); } private isAnonymousPath() { return this.http.anonymousPaths.isAnonymous(window.location.pathname); } }