diff --git a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts index 31d243fa699d9..fe5df81b927f2 100644 --- a/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts +++ b/src/plugins/data_view_field_editor/__jest__/client_integration/field_editor_flyout_preview.helpers.ts @@ -9,7 +9,7 @@ import { act } from 'react-dom/test-utils'; import { ReactWrapper } from 'enzyme'; import { registerTestBed, TestBed } from '@kbn/test-jest-helpers'; -import { API_BASE_PATH } from '../../common/constants'; +import { FIELD_PREVIEW_PATH } from '../../common/constants'; import { Context } from '../../public/components/field_editor_context'; import { FieldEditorFlyoutContent, @@ -118,7 +118,7 @@ const getActions = (testBed: TestBed) => { while (i >= 0) { const request = server.requests[i]; - if (request.method === 'POST' && request.url === `${API_BASE_PATH}/field_preview`) { + if (request.method === 'POST' && request.url === FIELD_PREVIEW_PATH) { return { ...request, requestBody: JSON.parse(JSON.parse(request.requestBody).body), diff --git a/src/plugins/data_view_field_editor/common/constants.ts b/src/plugins/data_view_field_editor/common/constants.ts index ecd6b1ddd408b..a22aab4bc22e5 100644 --- a/src/plugins/data_view_field_editor/common/constants.ts +++ b/src/plugins/data_view_field_editor/common/constants.ts @@ -6,4 +6,6 @@ * Side Public License, v 1. */ -export const API_BASE_PATH = '/api/index_pattern_field_editor'; +export const FIELD_PREVIEW_PATH = '/internal/data_view_field_editor/field_preview'; + +export const INITIAL_REST_VERSION = '1'; diff --git a/src/plugins/data_view_field_editor/public/lib/api.ts b/src/plugins/data_view_field_editor/public/lib/api.ts index 2dcfbff45e981..07cca82f9baf8 100644 --- a/src/plugins/data_view_field_editor/public/lib/api.ts +++ b/src/plugins/data_view_field_editor/public/lib/api.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ import { HttpSetup } from '@kbn/core/public'; -import { API_BASE_PATH } from '../../common/constants'; +import { + FIELD_PREVIEW_PATH as path, + INITIAL_REST_VERSION as version, +} from '../../common/constants'; import { sendRequest } from '../shared_imports'; import { PainlessExecuteContext, FieldPreviewResponse } from '../components/preview'; @@ -23,7 +26,7 @@ export const initApi = (httpClient: HttpSetup) => { document: Record; }) => { return sendRequest(httpClient, { - path: `${API_BASE_PATH}/field_preview`, + path, method: 'post', body: { index, @@ -31,6 +34,7 @@ export const initApi = (httpClient: HttpSetup) => { script, document, }, + version, }); }; diff --git a/src/plugins/data_view_field_editor/server/routes/field_preview.ts b/src/plugins/data_view_field_editor/server/routes/field_preview.ts index bee5fc0dbd1be..7d2525b273a86 100644 --- a/src/plugins/data_view_field_editor/server/routes/field_preview.ts +++ b/src/plugins/data_view_field_editor/server/routes/field_preview.ts @@ -7,9 +7,7 @@ */ import { schema } from '@kbn/config-schema'; -import { HttpResponsePayload } from '@kbn/core/server'; - -import { API_BASE_PATH } from '../../common/constants'; +import { FIELD_PREVIEW_PATH as path } from '../../common/constants'; import { RouteDependencies } from '../types'; import { handleEsError } from '../shared_imports'; @@ -29,12 +27,35 @@ const bodySchema = schema.object({ document: schema.object({}, { unknowns: 'allow' }), }); +const geoPoint = schema.object({ + type: schema.literal('Point'), + coordinates: schema.arrayOf(schema.number(), { minSize: 2, maxSize: 2 }), +}); + +const valueSchema = schema.oneOf([schema.boolean(), schema.number(), schema.string(), geoPoint]); + export const registerFieldPreviewRoute = ({ router }: RouteDependencies): void => { - router.post( + router.versioned.post({ path, access: 'internal' }).addVersion( { - path: `${API_BASE_PATH}/field_preview`, + version: '1', validate: { - body: bodySchema, + request: { + body: bodySchema, + }, + response: { + 200: { + body: schema.object({ + values: schema.oneOf([ + // composite field + schema.recordOf(schema.string(), schema.arrayOf(valueSchema)), + // primitive field + schema.arrayOf(valueSchema), + ]), + error: schema.maybe(schema.object({}, { unknowns: 'allow' })), + status: schema.maybe(schema.number()), + }), + }, + }, }, }, async (ctx, req, res) => { @@ -51,17 +72,17 @@ export const registerFieldPreviewRoute = ({ router }: RouteDependencies): void = try { // client types need to be update to support this request format + // when it does, supply response types // @ts-expect-error const { result } = await client.asCurrentUser.scriptsPainlessExecute(body); - const fieldValue = result as HttpResponsePayload; - return res.ok({ body: { values: fieldValue } }); + return res.ok({ body: { values: result } }); } catch (error) { // Assume invalid painless script was submitted // Return 200 with error object const handleCustomError = () => { return res.ok({ - body: { values: [], ...error.body }, + body: { values: [], error: error.body?.error, status: error.statusCode }, }); }; diff --git a/test/api_integration/apis/data_view_field_editor/constants.ts b/test/api_integration/apis/data_view_field_editor/constants.ts deleted file mode 100644 index ecd6b1ddd408b..0000000000000 --- a/test/api_integration/apis/data_view_field_editor/constants.ts +++ /dev/null @@ -1,9 +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 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 or the Server - * Side Public License, v 1. - */ - -export const API_BASE_PATH = '/api/index_pattern_field_editor'; diff --git a/test/api_integration/apis/data_view_field_editor/field_preview.ts b/test/api_integration/apis/data_view_field_editor/field_preview.ts index d1943983683f1..28b66fc53d60c 100644 --- a/test/api_integration/apis/data_view_field_editor/field_preview.ts +++ b/test/api_integration/apis/data_view_field_editor/field_preview.ts @@ -8,9 +8,13 @@ import expect from '@kbn/expect'; +import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common'; import { getErrorCodeFromErrorReason } from '@kbn/data-view-field-editor-plugin/public/lib/runtime_field_validation'; +import { + FIELD_PREVIEW_PATH, + INITIAL_REST_VERSION, +} from '@kbn/data-view-field-editor-plugin/common/constants'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { API_BASE_PATH } from './constants'; const INDEX_NAME = 'api-integration-test-field-preview'; @@ -83,7 +87,8 @@ export default function ({ getService }: FtrProviderContext) { }; const { body: response } = await supertest - .post(`${API_BASE_PATH}/field_preview`) + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) .send(payload) .set('kbn-xsrf', 'xxx') .expect(200); @@ -96,7 +101,8 @@ export default function ({ getService }: FtrProviderContext) { describe('payload validation', () => { it('should require a script', async () => { await supertest - .post(`${API_BASE_PATH}/field_preview`) + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) .send({ context: 'keyword_field', index: INDEX_NAME, @@ -107,7 +113,8 @@ export default function ({ getService }: FtrProviderContext) { it('should require a context', async () => { await supertest - .post(`${API_BASE_PATH}/field_preview`) + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) .send({ script: { source: 'emit("hello")' }, index: INDEX_NAME, @@ -118,7 +125,8 @@ export default function ({ getService }: FtrProviderContext) { it('should require an index', async () => { await supertest - .post(`${API_BASE_PATH}/field_preview`) + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) .send({ script: { source: 'emit("hello")' }, context: 'keyword_field', @@ -134,7 +142,8 @@ export default function ({ getService }: FtrProviderContext) { // If this test fail we'll need to update the "getErrorCodeFromErrorReason()" handler it('should detect a script casting error', async () => { const { body: response } = await supertest - .post(`${API_BASE_PATH}/field_preview`) + .post(FIELD_PREVIEW_PATH) + .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION) .send({ script: { source: 'emit(123)' }, // We send a long but the type is "keyword" context: 'keyword_field',