diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/scope/worker/apis/spaces/index.ts b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/scope/worker/apis/spaces/index.ts index d7ebd3db94034..cb13c317c21cd 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/scope/worker/apis/spaces/index.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/scope/worker/apis/spaces/index.ts @@ -11,18 +11,18 @@ import type { KbnClient, ScoutLogger } from '../../../../../../common'; import { measurePerformanceAsync } from '../../../../../../common'; export interface SpacesApiService { - create: (space: { id: string; name: string }) => Promise; + create: (space: { id: string; name?: string; disabledFeatures?: string[] }) => Promise; delete: (id: string) => Promise; } export const getSpacesApiHelper = (log: ScoutLogger, kbnClient: KbnClient): SpacesApiService => { return { - create: async (space: { id: string; name: string }) => { - await measurePerformanceAsync(log, `spacesApi.create(${space.id})`, async () => { + create: async ({ id, name = id, disabledFeatures = [] }) => { + await measurePerformanceAsync(log, `spacesApi.create(${id})`, async () => { await kbnClient.request({ method: 'POST', path: '/api/spaces/space', - body: space, + body: { id, name, disabledFeatures }, }); }); }, diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/README.md b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/README.md index e68a39d955007..78ffa1c391ed5 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/README.md +++ b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/README.md @@ -48,12 +48,14 @@ Custom asymmetric matchers for use with `toMatchObject`: - **`expect.toBeGreaterThan(n)`** - Matches if value > n - **`expect.toBeLessThan(n)`** - Matches if value < n +- **`expect.stringContaining(s)`** - Matches a string that contains the given substring ```typescript expect(response).toMatchObject({ body: { count: expect.toBeGreaterThan(0), limit: expect.toBeLessThan(100), + message: expect.stringContaining('method [get] exists'), }, }); ``` @@ -87,6 +89,7 @@ The following [Playwright matchers](https://playwright.dev/docs/api/class-generi - `expect.arrayContaining(array)` - array contains all expected elements - `expect.objectContaining(object)` - object contains expected properties +- `expect.stringContaining(string)` - string contains the expected substring --- diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.test.ts b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.test.ts index e08f62930da71..de6e44cec73c7 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.test.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.test.ts @@ -32,6 +32,27 @@ describe('custom asymmetric matchers', () => { }); }); + describe('expect.stringContaining()', () => { + it('matches strings that contain the expected substring', () => { + expect(() => + apiExpect({ message: 'hello world' }).toMatchObject({ + message: apiExpect.stringContaining('hello'), + }) + ).not.toThrow(); + expect(() => + apiExpect({ message: 'hello world' }).toMatchObject({ + message: apiExpect.stringContaining('missing'), + }) + ).toThrow(); + }); + + it('does not match non-string values', () => { + expect(() => + apiExpect({ code: 404 }).toMatchObject({ code: apiExpect.stringContaining('4') }) + ).toThrow(); + }); + }); + it('supports nesting and combining with Playwright matchers', () => { const response = { data: { diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.ts b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.ts index 3f1e1c5f6d3e1..f32f3204cfc50 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/asymmetric_matchers.ts @@ -56,4 +56,11 @@ export const asymmetricMatchers: AsymmetricMatchers = { /** Matches an object that contains and matches all of the properties in the expected object */ objectContaining: >(expected: T) => baseExpect.objectContaining(expected), + + /** Matches a string that contains the expected substring */ + stringContaining: (expected: string) => + createAsymmetricMatcher( + (actual) => typeof actual === 'string' && actual.includes(expected), + `stringContaining(${JSON.stringify(expected)})` + ), }; diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/types.ts b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/types.ts index 7f301eb4c709a..f07c2d80f64fc 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/types.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/matchers/api/types.ts @@ -110,4 +110,6 @@ export interface AsymmetricMatchers { arrayContaining(expected: T[]): AsymmetricMatcher; /** Matches an object that contains and matches all of the properties in the expected object */ objectContaining>(expected: T): AsymmetricMatcher; + /** Matches a string that contains the expected substring */ + stringContaining(expected: string): AsymmetricMatcher; } diff --git a/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts b/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts index 1bcde616441fb..8c9f73b80f2ce 100644 --- a/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts +++ b/src/platform/plugins/shared/data_views/test/scout/api/fixtures/constants.ts @@ -29,3 +29,11 @@ export const SERVICE_KEY_LEGACY = 'index_pattern'; export const SERVICE_KEY = 'data_view'; export const ID_OVER_MAX_LENGTH = 'x'.repeat(1759); + +export const INTERNAL_COMMON_HEADERS = { + 'kbn-xsrf': 'some-xsrf-token', + [X_ELASTIC_INTERNAL_ORIGIN_REQUEST]: 'kibana', + [ELASTIC_HTTP_VERSION_HEADER]: '1', +}; + +export const FIELDS_FOR_WILDCARD_PATH = 'internal/data_views/_fields_for_wildcard'; diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/rollup_data_views.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/rollup_data_views.spec.ts new file mode 100644 index 0000000000000..9bf189cc63586 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/data_views/rollup_data_views.spec.ts @@ -0,0 +1,53 @@ +/* + * 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 { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + ES_ARCHIVE_BASIC_INDEX, + FIELDS_FOR_WILDCARD_PATH, + INTERNAL_COMMON_HEADERS, +} from '../../fixtures/constants'; + +apiTest.describe( + 'rollup data views - fields for wildcard', + { + tag: [ + ...tags.serverless.observability.complete, + ...tags.serverless.search, + ...tags.serverless.security.complete, + ], + }, + () => { + let adminApiCredentials: RoleApiCredentials; + + apiTest.beforeAll(async ({ requestAuth, esArchiver }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest( + 'returns 200 and best effort response despite lack of rollup support', + async ({ apiClient }) => { + const response = await apiClient.get( + `${FIELDS_FOR_WILDCARD_PATH}?pattern=basic_index&type=rollup&rollup_index=bar`, + { + headers: { + ...INTERNAL_COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + } + ); + + expect(response).toHaveStatusCode(200); + expect(response.body.fields).toHaveLength(5); + } + ); + } +); diff --git a/src/platform/plugins/shared/data_views/test/scout/api/tests/scripted_fields_disabled.spec.ts b/src/platform/plugins/shared/data_views/test/scout/api/tests/scripted_fields_disabled.spec.ts new file mode 100644 index 0000000000000..6d00c075caf30 --- /dev/null +++ b/src/platform/plugins/shared/data_views/test/scout/api/tests/scripted_fields_disabled.spec.ts @@ -0,0 +1,71 @@ +/* + * 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 { apiTest, tags, type RoleApiCredentials } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; +import { + COMMON_HEADERS, + DATA_VIEW_PATH, + ES_ARCHIVE_BASIC_INDEX, + SERVICE_KEY, +} from '../fixtures/constants'; + +apiTest.describe( + 'scripted fields disabled', + { + tag: [ + ...tags.serverless.observability.complete, + ...tags.serverless.search, + ...tags.serverless.security.complete, + ], + }, + () => { + let adminApiCredentials: RoleApiCredentials; + let createdDataViewId: string | undefined; + + apiTest.beforeAll(async ({ requestAuth, esArchiver, kbnClient }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + await kbnClient.savedObjects.cleanStandardList(); + await esArchiver.loadIfNeeded(ES_ARCHIVE_BASIC_INDEX); + }); + + apiTest.afterEach(async ({ apiServices }) => { + if (createdDataViewId) { + await apiServices.dataViews.delete(createdDataViewId); + createdDataViewId = undefined; + } + }); + + apiTest('scripted fields are ignored when disabled', async ({ apiClient }) => { + const response = await apiClient.post(DATA_VIEW_PATH, { + headers: { + ...COMMON_HEADERS, + ...adminApiCredentials.apiKeyHeader, + }, + body: { + [SERVICE_KEY]: { + title: 'basic_index', + fields: { + foo_scripted: { + name: 'foo_scripted', + type: 'string', + scripted: true, + script: "doc['field_name'].value", + }, + }, + }, + }, + }); + + expect(response).toHaveStatusCode(200); + createdDataViewId = response.body[SERVICE_KEY].id; + expect(response.body[SERVICE_KEY].fields.foo_scripted).toBeUndefined(); + }); + } +); diff --git a/x-pack/platform/plugins/shared/spaces/test/scout/api/constants.ts b/x-pack/platform/plugins/shared/spaces/test/scout/api/constants.ts index 017497eb57a73..27ddfedbfd316 100644 --- a/x-pack/platform/plugins/shared/spaces/test/scout/api/constants.ts +++ b/x-pack/platform/plugins/shared/spaces/test/scout/api/constants.ts @@ -41,3 +41,7 @@ export const COMMON_HEADERS = { 'x-elastic-internal-origin': 'kibana', 'Content-Type': 'application/json;charset=UTF-8', }; + +export const COMMON_HEADERS_NO_INTERNAL = { + 'kbn-xsrf': 'some-xsrf-token', +}; diff --git a/x-pack/platform/plugins/shared/spaces/test/scout/api/fixtures/index.ts b/x-pack/platform/plugins/shared/spaces/test/scout/api/fixtures/index.ts index 2d7d44f3f0f03..891457feb7932 100644 --- a/x-pack/platform/plugins/shared/spaces/test/scout/api/fixtures/index.ts +++ b/x-pack/platform/plugins/shared/spaces/test/scout/api/fixtures/index.ts @@ -16,11 +16,9 @@ export interface SpacesRequestAuthFixture extends RequestAuthFixture { getSavedObjectsManagementApiKey: () => Promise; } -export interface SpacesApiFixtures { +export const apiTest = base.extend<{ requestAuth: SpacesRequestAuthFixture; -} - -export const apiTest = base.extend({ +}>({ requestAuth: async ({ requestAuth }, use) => { const getSavedObjectsManagementApiKey = async (): Promise => { return await requestAuth.getApiKeyForCustomRole({ @@ -34,17 +32,13 @@ export const apiTest = base.extend({ feature: { savedObjectsManagement: ['all'], }, - spaces: ['*'], // Access to all spaces + spaces: ['*'], }, ], }); }; - const extendedRequestAuth: SpacesRequestAuthFixture = { - ...requestAuth, - getSavedObjectsManagementApiKey, - }; - - await use(extendedRequestAuth); + const extended: SpacesRequestAuthFixture = { ...requestAuth, getSavedObjectsManagementApiKey }; + await use(extended); }, }); diff --git a/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/active_space.spec.ts b/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/active_space.spec.ts new file mode 100644 index 0000000000000..3dcebd99a304d --- /dev/null +++ b/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/active_space.spec.ts @@ -0,0 +1,84 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; + +import { COMMON_HEADERS } from '../constants'; +import { apiTest } from '../fixtures'; + +apiTest.describe( + 'Get active space', + { + tag: tags.deploymentAgnostic, + }, + () => { + let cookieHeader: Record; + + apiTest.beforeAll(async ({ apiServices, samlAuth }) => { + ({ cookieHeader } = await samlAuth.asInteractiveUser('admin')); + await apiServices.spaces.create({ id: 'foo-space' }); + }); + + apiTest.afterAll(async ({ apiServices }) => { + await apiServices.spaces.delete('foo-space'); + }); + + apiTest('returns the default space', async ({ apiClient }) => { + const response = await apiClient.get('internal/spaces/_active_space', { + headers: { ...COMMON_HEADERS, ...cookieHeader }, + }); + + expect(response).toHaveStatusCode(200); + const { id, name, _reserved } = response.body; + expect({ id, name, _reserved }).toStrictEqual({ + id: 'default', + name: 'Default', + _reserved: true, + }); + }); + + apiTest('returns the default space when explicitly referenced', async ({ apiClient }) => { + const response = await apiClient.get('s/default/internal/spaces/_active_space', { + headers: { ...COMMON_HEADERS, ...cookieHeader }, + }); + + expect(response).toHaveStatusCode(200); + const { id, name, _reserved } = response.body; + expect({ id, name, _reserved }).toStrictEqual({ + id: 'default', + name: 'Default', + _reserved: true, + }); + }); + + apiTest('returns the foo space', async ({ apiClient }) => { + const { body } = await apiClient.get('s/foo-space/internal/spaces/_active_space', { + headers: { ...COMMON_HEADERS, ...cookieHeader }, + }); + + expect(body).toMatchObject({ + id: 'foo-space', + name: 'foo-space', + disabledFeatures: [], + }); + }); + + apiTest('returns 404 when the space is not found', async ({ apiClient }) => { + const response = await apiClient.get('s/not-found-space/internal/spaces/_active_space', { + headers: { ...COMMON_HEADERS, ...cookieHeader }, + }); + + expect(response).toHaveStatusCode(404); + expect(response.body).toStrictEqual({ + statusCode: 404, + error: 'Not Found', + message: 'Saved object [space/not-found-space] not found', + }); + }); + } +); diff --git a/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/serverless_only/crud.spec.ts b/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/serverless_only/crud.spec.ts new file mode 100644 index 0000000000000..5cb6435781779 --- /dev/null +++ b/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/serverless_only/crud.spec.ts @@ -0,0 +1,149 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RoleApiCredentials } from '@kbn/scout'; +import { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; + +import { COMMON_HEADERS } from '../../constants'; +import { apiTest } from '../../fixtures'; + +/** + * Serverless-specific spaces CRUD tests. + * Feature visibility cannot be set in serverless — the create/update tests + * verifying that disabledFeatures is rejected are unique to serverless. + */ +apiTest.describe( + 'Spaces CRUD', + { + tag: [ + ...tags.serverless.observability.complete, + ...tags.serverless.search, + ...tags.serverless.security.complete, + ], + }, + () => { + let adminApiCredentials: RoleApiCredentials; + const createdSpaceIds: string[] = []; + + apiTest.beforeAll(async ({ requestAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + }); + + apiTest.afterAll(async ({ apiServices }) => { + for (const id of createdSpaceIds) { + await apiServices.spaces.delete(id); + } + }); + + apiTest('should allow us to create a space', async ({ apiClient }) => { + const response = await apiClient.post('api/spaces/space', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + body: { id: 'custom_space_1', name: 'custom_space_1', disabledFeatures: [] }, + }); + + expect(response).toHaveStatusCode(200); + createdSpaceIds.push('custom_space_1'); + }); + + apiTest( + 'should not allow us to create a space with disabled features', + async ({ apiClient }) => { + const response = await apiClient.post('api/spaces/space', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + body: { id: 'custom_space_2', name: 'custom_space_2', disabledFeatures: ['discover'] }, + }); + + expect(response).toHaveStatusCode(400); + } + ); + + apiTest('should allow us to get a space', async ({ apiClient, apiServices }) => { + await apiServices.spaces.create({ id: 'space_to_get_1' }); + createdSpaceIds.push('space_to_get_1'); + + const { body } = await apiClient.get('api/spaces/space/space_to_get_1', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(body).toMatchObject({ + id: 'space_to_get_1', + name: 'space_to_get_1', + disabledFeatures: [], + }); + }); + + apiTest('should allow us to get all spaces', async ({ apiClient }) => { + const { body } = await apiClient.get('api/spaces/space', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(body).toStrictEqual( + expect.arrayContaining([ + expect.objectContaining({ id: 'default', name: 'Default', _reserved: true }), + ]) + ); + }); + + apiTest('should allow us to update a space', async ({ apiClient, apiServices }) => { + await apiServices.spaces.create({ id: 'space_to_update' }); + createdSpaceIds.push('space_to_update'); + + const updateResponse = await apiClient.put('api/spaces/space/space_to_update', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + body: { + id: 'space_to_update', + name: 'some new name', + initials: 'SN', + disabledFeatures: [], + }, + }); + expect(updateResponse).toHaveStatusCode(200); + + const { body } = await apiClient.get('api/spaces/space/space_to_update', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(body).toMatchObject({ + id: 'space_to_update', + name: 'some new name', + initials: 'SN', + disabledFeatures: [], + }); + }); + + apiTest( + 'should not allow us to update a space with disabled features', + async ({ apiClient, apiServices }) => { + await apiServices.spaces.create({ id: 'space_to_update_disabled' }); + createdSpaceIds.push('space_to_update_disabled'); + + const response = await apiClient.put('api/spaces/space/space_to_update_disabled', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + body: { + id: 'space_to_update_disabled', + name: 'some new name', + initials: 'SN', + disabledFeatures: ['discover'], + }, + }); + + expect(response).toHaveStatusCode(400); + } + ); + + apiTest('should allow us to delete a space', async ({ apiClient, apiServices }) => { + await apiServices.spaces.create({ id: 'space_to_delete' }); + + const response = await apiClient.delete('api/spaces/space/space_to_delete', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(response).toHaveStatusCode(204); + }); + } +); diff --git a/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/serverless_only/route_access.spec.ts b/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/serverless_only/route_access.spec.ts new file mode 100644 index 0000000000000..f3f542e79bd18 --- /dev/null +++ b/x-pack/platform/plugins/shared/spaces/test/scout/api/tests/serverless_only/route_access.spec.ts @@ -0,0 +1,166 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RoleApiCredentials } from '@kbn/scout'; +import { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/api'; + +import { COMMON_HEADERS, COMMON_HEADERS_NO_INTERNAL } from '../../constants'; +import { apiTest } from '../../fixtures'; + +apiTest.describe( + 'Spaces route access', + { + tag: [ + ...tags.serverless.observability.complete, + ...tags.serverless.search, + ...tags.serverless.security.complete, + ], + }, + () => { + let adminApiCredentials: RoleApiCredentials; + let cookieHeader: Record; + + apiTest.beforeAll(async ({ requestAuth, samlAuth }) => { + adminApiCredentials = await requestAuth.getApiKey('admin'); + ({ cookieHeader } = await samlAuth.asInteractiveUser('admin')); + }); + + apiTest('#getActiveSpace requires internal header', async ({ apiClient }) => { + const rejectedResponse = await apiClient.get('internal/spaces/_active_space', { + headers: { ...COMMON_HEADERS_NO_INTERNAL, ...cookieHeader }, + }); + + expect(rejectedResponse.statusCode).toBe(400); + expect(rejectedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: expect.stringContaining( + 'method [get] exists but is not available with the current configuration' + ), + }); + + const acceptedResponse = await apiClient.get('internal/spaces/_active_space', { + headers: { ...COMMON_HEADERS, ...cookieHeader }, + }); + + expect(acceptedResponse.statusCode).toBe(200); + expect(acceptedResponse.body).toMatchObject({ id: 'default' }); + }); + + apiTest('#copyToSpace requires internal header', async ({ apiClient }) => { + const rejectedResponse = await apiClient.post('api/spaces/_copy_saved_objects', { + headers: { ...COMMON_HEADERS_NO_INTERNAL, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(rejectedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: expect.stringContaining( + 'method [post] exists but is not available with the current configuration' + ), + }); + + const acceptedResponse = await apiClient.post('api/spaces/_copy_saved_objects', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(acceptedResponse.statusCode).toBe(400); + expect(acceptedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: '[request body]: expected a plain object value, but found [null] instead.', + }); + }); + + apiTest('#resolveCopyToSpaceErrors requires internal header', async ({ apiClient }) => { + const rejectedResponse = await apiClient.post( + 'api/spaces/_resolve_copy_saved_objects_errors', + { + headers: { ...COMMON_HEADERS_NO_INTERNAL, ...adminApiCredentials.apiKeyHeader }, + } + ); + + expect(rejectedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: expect.stringContaining( + 'method [post] exists but is not available with the current configuration' + ), + }); + + const acceptedResponse = await apiClient.post( + 'api/spaces/_resolve_copy_saved_objects_errors', + { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + } + ); + + expect(acceptedResponse.statusCode).toBe(400); + expect(acceptedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: '[request body]: expected a plain object value, but found [null] instead.', + }); + }); + + apiTest('#updateObjectsSpaces requires internal header', async ({ apiClient }) => { + const rejectedResponse = await apiClient.post('api/spaces/_update_objects_spaces', { + headers: { ...COMMON_HEADERS_NO_INTERNAL, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(rejectedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: expect.stringContaining( + 'method [post] exists but is not available with the current configuration' + ), + }); + + const acceptedResponse = await apiClient.post('api/spaces/_update_objects_spaces', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(acceptedResponse.statusCode).toBe(400); + expect(acceptedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: '[request body]: expected a plain object value, but found [null] instead.', + }); + }); + + apiTest('#getShareableReferences requires internal header', async ({ apiClient }) => { + const rejectedResponse = await apiClient.post('api/spaces/_get_shareable_references', { + headers: { ...COMMON_HEADERS_NO_INTERNAL, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(rejectedResponse.body).toStrictEqual({ + statusCode: 400, + error: 'Bad Request', + message: expect.stringContaining( + 'method [post] exists but is not available with the current configuration' + ), + }); + + const acceptedResponse = await apiClient.post('api/spaces/_get_shareable_references', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + body: { objects: [{ type: 'a', id: 'a' }] }, + }); + + expect(acceptedResponse.statusCode).toBe(200); + }); + + apiTest('#disableLegacyUrlAliases is disabled', async ({ apiClient }) => { + const response = await apiClient.post('api/spaces/_disable_legacy_url_aliases', { + headers: { ...COMMON_HEADERS, ...adminApiCredentials.apiKeyHeader }, + }); + + expect(response.statusCode).toBe(404); + expect(response.body).toMatchObject({ statusCode: 404 }); + }); + } +); diff --git a/x-pack/platform/test/api_integration/apis/spaces/get_active_space.ts b/x-pack/platform/test/api_integration/apis/spaces/get_active_space.ts deleted file mode 100644 index dc9b0fbbb9dec..0000000000000 --- a/x-pack/platform/test/api_integration/apis/spaces/get_active_space.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common'; -import type { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const spacesService = getService('spaces'); - - describe('GET /internal/spaces/_active_space', () => { - before(async () => { - await spacesService.create({ - id: 'foo-space', - name: 'Foo Space', - disabledFeatures: [], - color: '#AABBCC', - }); - }); - - after(async () => { - await spacesService.delete('foo-space'); - }); - - it('returns the default space', async () => { - await supertest - .get('/internal/spaces/_active_space') - .set('kbn-xsrf', 'xxx') - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') - .expect(200) - .then((response) => { - const { id, name, _reserved } = response.body; - expect({ id, name, _reserved }).to.eql({ - id: 'default', - name: 'Default', - _reserved: true, - }); - }); - }); - - it('returns the default space when explicitly referenced', async () => { - await supertest - .get('/s/default/internal/spaces/_active_space') - .set('kbn-xsrf', 'xxx') - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') - .expect(200) - .then((response) => { - const { id, name, _reserved } = response.body; - expect({ id, name, _reserved }).to.eql({ - id: 'default', - name: 'Default', - _reserved: true, - }); - }); - }); - - it('returns the foo space', async () => { - await supertest - .get('/s/foo-space/internal/spaces/_active_space') - .set('kbn-xsrf', 'xxx') - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') - .expect(200, { - id: 'foo-space', - name: 'Foo Space', - disabledFeatures: [], - color: '#AABBCC', - }); - }); - - it('returns 404 when the space is not found', async () => { - await supertest - .get('/s/not-found-space/internal/spaces/_active_space') - .set('kbn-xsrf', 'xxx') - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') - .expect(404, { - statusCode: 404, - error: 'Not Found', - message: 'Saved object [space/not-found-space] not found', - }); - }); - }); -} diff --git a/x-pack/platform/test/api_integration/apis/spaces/index.ts b/x-pack/platform/test/api_integration/apis/spaces/index.ts index 6e2a1aee43af8..a5a06f7f1621d 100644 --- a/x-pack/platform/test/api_integration/apis/spaces/index.ts +++ b/x-pack/platform/test/api_integration/apis/spaces/index.ts @@ -9,7 +9,6 @@ import type { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('spaces', function () { - loadTestFile(require.resolve('./get_active_space')); loadTestFile(require.resolve('./saved_objects')); loadTestFile(require.resolve('./space_attributes')); loadTestFile(require.resolve('./get_content_summary')); diff --git a/x-pack/platform/test/serverless/api_integration/configs/observability/config.group1.ts b/x-pack/platform/test/serverless/api_integration/configs/observability/config.group1.ts index 1c20460f02681..e792a79778e32 100644 --- a/x-pack/platform/test/serverless/api_integration/configs/observability/config.group1.ts +++ b/x-pack/platform/test/serverless/api_integration/configs/observability/config.group1.ts @@ -17,7 +17,6 @@ export default createTestConfig({ require.resolve('../../test_suites/elasticsearch_api'), require.resolve('../../test_suites/index_management'), require.resolve('../../test_suites/kql_telemetry'), - require.resolve('../../test_suites/management'), require.resolve('../../test_suites/platform_security'), require.resolve('../../test_suites/scripts_tests'), require.resolve('../../test_suites/search_oss'), diff --git a/x-pack/platform/test/serverless/api_integration/configs/search/config.group1.ts b/x-pack/platform/test/serverless/api_integration/configs/search/config.group1.ts index 1efe32bec8f14..bfbd13bd95117 100644 --- a/x-pack/platform/test/serverless/api_integration/configs/search/config.group1.ts +++ b/x-pack/platform/test/serverless/api_integration/configs/search/config.group1.ts @@ -17,7 +17,6 @@ export default createTestConfig({ require.resolve('../../test_suites/elasticsearch_api'), require.resolve('../../test_suites/index_management'), require.resolve('../../test_suites/kql_telemetry'), - require.resolve('../../test_suites/management'), require.resolve('../../test_suites/platform_security'), require.resolve('../../test_suites/scripts_tests'), require.resolve('../../test_suites/search_oss'), diff --git a/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts b/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts index c8db31caec166..0af0223c84e38 100644 --- a/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts +++ b/x-pack/platform/test/serverless/api_integration/configs/security/config.group1.ts @@ -17,7 +17,6 @@ export default createTestConfig({ require.resolve('../../test_suites/elasticsearch_api'), require.resolve('../../test_suites/index_management'), require.resolve('../../test_suites/kql_telemetry'), - require.resolve('../../test_suites/management'), require.resolve('../../test_suites/platform_security'), require.resolve('../../test_suites/scripts_tests'), require.resolve('../../test_suites/search_oss'), diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/management/index.ts b/x-pack/platform/test/serverless/api_integration/test_suites/management/index.ts deleted file mode 100644 index 3c90812bbc537..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/management/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ loadTestFile }: FtrProviderContext) { - describe('Management', function () { - this.tags(['esGate']); - - loadTestFile(require.resolve('./rollups')); - loadTestFile(require.resolve('./scripted_fields')); - loadTestFile(require.resolve('./spaces')); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/management/rollups.ts b/x-pack/platform/test/serverless/api_integration/test_suites/management/rollups.ts deleted file mode 100644 index 4818fd9030500..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/management/rollups.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from 'expect'; -import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; -import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants'; -import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common/src/constants'; -import { FIELDS_FOR_WILDCARD_PATH as BASE_URI } from '@kbn/data-views-plugin/common/constants'; -import { DataViewType } from '@kbn/data-views-plugin/common'; -import type { FtrProviderContext } from '../../ftr_provider_context'; -import type { InternalRequestHeader, RoleCredentials } from '../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('rollup data views - fields for wildcard', function () { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - it('returns 200 and best effort response despite lack of rollup support', async () => { - const response = await supertestWithoutAuth - .get(BASE_URI) - .query({ - pattern: 'basic_index', - type: DataViewType.ROLLUP, - rollup_index: 'bar', - }) - .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'true') - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader); - - expect(response.status).toBe(200); - expect(response.body.fields.length).toEqual(5); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/management/scripted_fields.ts b/x-pack/platform/test/serverless/api_integration/test_suites/management/scripted_fields.ts deleted file mode 100644 index 5ef8d2757295f..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/management/scripted_fields.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from 'expect'; -import { DATA_VIEW_PATH } from '@kbn/data-views-plugin/server'; -import type { FtrProviderContext } from '../../ftr_provider_context'; -import type { InternalRequestHeader, RoleCredentials } from '../../../shared/services'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const kibanaServer = getService('kibanaServer'); - const svlCommonApi = getService('svlCommonApi'); - const svlUserManager = getService('svlUserManager'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); - let roleAuthc: RoleCredentials; - let internalReqHeader: InternalRequestHeader; - - describe('scripted fields disabled', function () { - before(async () => { - roleAuthc = await svlUserManager.createM2mApiKeyWithRoleScope('admin'); - internalReqHeader = svlCommonApi.getInternalRequestHeader(); - // TODO: We're running into a 'Duplicate data view: basic_index' - // error in Serverless, so make sure to clean up first - await kibanaServer.savedObjects.cleanStandardList(); - await esArchiver.load( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - }); - - after(async () => { - await esArchiver.unload( - 'src/platform/test/api_integration/fixtures/es_archiver/index_patterns/basic_index' - ); - await svlUserManager.invalidateM2mApiKeyWithRoleScope(roleAuthc); - }); - it('scripted fields are ignored when disabled', async () => { - const response = await supertestWithoutAuth - .post(DATA_VIEW_PATH) - .set(internalReqHeader) - .set(roleAuthc.apiKeyHeader) - .send({ - data_view: { - title: 'basic_index', - fields: { - foo_scripted: { - name: 'foo_scripted', - type: 'string', - scripted: true, - script: "doc['field_name'].value", - }, - }, - }, - }); - - expect(response.status).toBe(200); - expect(response.body.data_view.fields.foo_scripted).toBeUndefined(); - }); - }); -} diff --git a/x-pack/platform/test/serverless/api_integration/test_suites/management/spaces.ts b/x-pack/platform/test/serverless/api_integration/test_suites/management/spaces.ts deleted file mode 100644 index 12f1bfd975194..0000000000000 --- a/x-pack/platform/test/serverless/api_integration/test_suites/management/spaces.ts +++ /dev/null @@ -1,431 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from 'expect'; -import { asyncForEach } from '@kbn/std'; -import type { SupertestWithRoleScopeType } from '../../services'; -import type { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const svlCommonApi = getService('svlCommonApi'); - const samlAuth = getService('samlAuth'); - const roleScopedSupertest = getService('roleScopedSupertest'); - let supertestAdminWithApiKey: SupertestWithRoleScopeType; - let supertestAdminWithCookieCredentials: SupertestWithRoleScopeType; - - async function createSpace(id: string) { - await supertestAdminWithApiKey - .post('/api/spaces/space') - .send({ - id, - name: id, - disabledFeatures: [], - }) - .expect(200); - } - - async function deleteSpace(id: string) { - await supertestAdminWithApiKey.delete(`/api/spaces/space/${id}`).expect(204); - } - - describe('spaces', function () { - before(async () => { - // admin is the only predefined role that will work for all 3 solutions - supertestAdminWithApiKey = await roleScopedSupertest.getSupertestWithRoleScope('admin', { - withCommonHeaders: true, - }); - supertestAdminWithCookieCredentials = await roleScopedSupertest.getSupertestWithRoleScope( - 'admin', - { - useCookieHeader: true, - } - ); - }); - - after(async () => { - await supertestAdminWithApiKey.destroy(); - }); - - // The create and update test cases are unique to serverless because - // setting feature visibility is not possible in serverless - describe('CRUD', () => { - after(async () => { - // delete any lingering spaces - const { body } = await supertestAdminWithApiKey.get('/api/spaces/space').send().expect(200); - - const toDelete = (body as Array<{ id: string }>).filter((f) => f.id !== 'default'); - - await asyncForEach(toDelete, async (space) => { - await deleteSpace(space.id); - }); - }); - - describe('Create (POST /api/spaces/space)', () => { - it('should allow us to create a space', async () => { - await supertestAdminWithApiKey - .post('/api/spaces/space') - .send({ - id: 'custom_space_1', - name: 'custom_space_1', - disabledFeatures: [], - }) - .expect(200); - }); - - it('should not allow us to create a space with disabled features', async () => { - await supertestAdminWithApiKey - .post('/api/spaces/space') - .send({ - id: 'custom_space_2', - name: 'custom_space_2', - disabledFeatures: ['discover'], - }) - .expect(400); - }); - }); - - describe('Read (GET /api/spaces/space)', () => { - before(async () => { - await createSpace('space_to_get_1'); - await createSpace('space_to_get_2'); - await createSpace('space_to_get_3'); - }); - - after(async () => { - await deleteSpace('space_to_get_1'); - await deleteSpace('space_to_get_2'); - await deleteSpace('space_to_get_3'); - }); - - it('should allow us to get a space', async () => { - const { body } = await supertestAdminWithApiKey - .get('/api/spaces/space/space_to_get_1') - .send() - .expect(200); - - expect(body).toEqual( - expect.objectContaining({ - id: 'space_to_get_1', - name: 'space_to_get_1', - disabledFeatures: [], - }) - ); - }); - - it('should allow us to get all spaces', async () => { - const { body } = await supertestAdminWithApiKey - .get('/api/spaces/space') - .send() - .expect(200); - - expect(body).toEqual( - expect.arrayContaining([ - { - _reserved: true, - color: '#00bfb3', - description: 'This is your default space!', - disabledFeatures: [], - id: 'default', - name: 'Default', - }, - { id: 'space_to_get_1', name: 'space_to_get_1', disabledFeatures: [] }, - { id: 'space_to_get_2', name: 'space_to_get_2', disabledFeatures: [] }, - { id: 'space_to_get_3', name: 'space_to_get_3', disabledFeatures: [] }, - ]) - ); - }); - }); - - describe('Update (PUT /api/spaces/space)', () => { - before(async () => { - await createSpace('space_to_update'); - }); - - after(async () => { - await deleteSpace('space_to_update'); - }); - - it('should allow us to update a space', async () => { - await supertestAdminWithApiKey - .put('/api/spaces/space/space_to_update') - .send({ - id: 'space_to_update', - name: 'some new name', - initials: 'SN', - disabledFeatures: [], - }) - .expect(200); - - const { body } = await supertestAdminWithApiKey - .get('/api/spaces/space/space_to_update') - .send() - .expect(200); - - expect(body).toEqual( - expect.objectContaining({ - id: 'space_to_update', - name: 'some new name', - initials: 'SN', - disabledFeatures: [], - }) - ); - }); - - it('should not allow us to update a space with disabled features', async () => { - await supertestAdminWithApiKey - .put('/api/spaces/space/space_to_update') - .send({ - id: 'space_to_update', - name: 'some new name', - initials: 'SN', - disabledFeatures: ['discover'], - }) - .expect(400); - }); - }); - - describe('Delete (DELETE /api/spaces/space)', () => { - it('should allow us to delete a space', async () => { - await createSpace('space_to_delete'); - - await supertestAdminWithApiKey.delete(`/api/spaces/space/space_to_delete`).expect(204); - }); - }); - - describe('Get active space (GET /internal/spaces/_active_space)', () => { - before(async () => { - await createSpace('foo-space'); - }); - - after(async () => { - await deleteSpace('foo-space'); - }); - - it('returns the default space', async () => { - const response = await supertestAdminWithCookieCredentials - .get('/internal/spaces/_active_space') - .set(samlAuth.getInternalRequestHeader()) - .expect(200); - - const { id, name, _reserved } = response.body; - expect({ id, name, _reserved }).toEqual({ - id: 'default', - name: 'Default', - _reserved: true, - }); - }); - - it('returns the default space when explicitly referenced', async () => { - const response = await supertestAdminWithCookieCredentials - .get('/s/default/internal/spaces/_active_space') - .set(samlAuth.getInternalRequestHeader()) - .expect(200); - - const { id, name, _reserved } = response.body; - expect({ id, name, _reserved }).toEqual({ - id: 'default', - name: 'Default', - _reserved: true, - }); - }); - - it('returns the foo space', async () => { - const { body } = await supertestAdminWithCookieCredentials - .get('/s/foo-space/internal/spaces/_active_space') - .set(samlAuth.getInternalRequestHeader()) - .expect(200); - - expect(body).toEqual( - expect.objectContaining({ - id: 'foo-space', - name: 'foo-space', - disabledFeatures: [], - }) - ); - }); - - it('returns 404 when the space is not found', async () => { - await supertestAdminWithCookieCredentials - .get('/s/not-found-space/internal/spaces/_active_space') - .set(samlAuth.getInternalRequestHeader()) - .expect(404, { - statusCode: 404, - error: 'Not Found', - message: 'Saved object [space/not-found-space] not found', - }); - }); - }); - }); - - describe('route access', () => { - // The 'internal route access' tests check that the internal header - // is needed for these specific endpoints. - // When accessed without internal headers they will return 400. - // They could be moved to deployment agnostic testing if there is - // a way to specify which tests to run when stateful vs serverles, - // as internal vs disabled is different in serverless. - describe('internal', () => { - it('#getActiveSpace requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithCookieCredentials - .get('/internal/spaces/_active_space') - .set(samlAuth.getCommonRequestHeader())); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [get] exists but is not available with the current configuration' - ), - }); - expect(status).toBe(400); - - ({ body, status } = await supertestAdminWithCookieCredentials - .get('/internal/spaces/_active_space') - .set(samlAuth.getInternalRequestHeader())); - // expect success because we're using the internal header - expect(body).toEqual( - expect.objectContaining({ - id: 'default', - }) - ); - expect(status).toBe(200); - }); - - it('#copyToSpace requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_copy_saved_objects' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_copy_saved_objects') - .set(samlAuth.getInternalRequestHeader())); - - svlCommonApi.assertResponseStatusCode(400, status, body); - - // expect 400 for missing body - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: '[request body]: expected a plain object value, but found [null] instead.', - }); - }); - - it('#resolveCopyToSpaceErrors requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_resolve_copy_saved_objects_errors' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_resolve_copy_saved_objects_errors') - .set(samlAuth.getInternalRequestHeader())); - - svlCommonApi.assertResponseStatusCode(400, status, body); - - // expect 400 for missing body - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: '[request body]: expected a plain object value, but found [null] instead.', - }); - }); - - it('#updateObjectsSpaces requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_update_objects_spaces' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_update_objects_spaces') - .set(samlAuth.getInternalRequestHeader())); - - svlCommonApi.assertResponseStatusCode(400, status, body); - - // expect 400 for missing body - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: '[request body]: expected a plain object value, but found [null] instead.', - }); - }); - - it('#getShareableReferences requires internal header', async () => { - let body: any; - let status: number; - - ({ body, status } = await supertestAdminWithApiKey.post( - '/api/spaces/_get_shareable_references' - )); - // expect a rejection because we're not using the internal header - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: expect.stringContaining( - 'method [post] exists but is not available with the current configuration' - ), - }); - - ({ body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_get_shareable_references') - .set(samlAuth.getInternalRequestHeader()) - .send({ - objects: [{ type: 'a', id: 'a' }], - })); - - svlCommonApi.assertResponseStatusCode(200, status, body); - }); - }); - - // Disabled in serverless, but public in stateful - describe('disabled', () => { - it('#disableLegacyUrlAliases', async () => { - const { body, status } = await supertestAdminWithApiKey - .post('/api/spaces/_disable_legacy_url_aliases') - .set(samlAuth.getInternalRequestHeader()); - - // without a request body we would normally a 400 bad request if the endpoint was registered - svlCommonApi.assertApiNotFound(body, status); - }); - }); - }); - }); -}