From 93f7a02b85710024253952dda116b77c7fb57663 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 17 Feb 2022 08:57:03 -0500 Subject: [PATCH] ExecutionContext: encode all context fields when converting to header (#125783) (#125924) * ExecutionContext: encode all context fields when converting to header value * switch back to using the vis type name instead of title for the execution context name * adapt FTR tests * fix server-side tests too (cherry picked from commit d047765c8b3cb79b433c84ea92d7cd20f4774be5) Co-authored-by: Pierre Gayvallet --- .../execution_context_container.test.ts | 30 ++++++++++++++-- .../execution_context_container.ts | 7 +++- .../integration_tests/tracing.test.ts | 36 +++++++++++++++++++ .../embeddable/visualize_embeddable.tsx | 2 +- .../tests/browser.ts | 17 ++++----- .../tests/server.ts | 4 +-- 6 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/core/server/execution_context/execution_context_container.test.ts b/src/core/server/execution_context/execution_context_container.test.ts index 8e9f46ee78a68..678c2e7134eca 100644 --- a/src/core/server/execution_context/execution_context_container.test.ts +++ b/src/core/server/execution_context/execution_context_container.test.ts @@ -91,16 +91,40 @@ describe('KibanaExecutionContext', () => { expect(value).toBe('type:name:41;child-test-type:child-test-name:42'); }); - it('returns an escaped string representation of provided execution contextStringified', () => { + it('returns an escaped string representation of provided execution context', () => { const context: KibanaExecutionContext = { id: 'Visualization☺漢字', + type: 'test☺type', + name: 'test漢name', + description: 'test字description', + }; + + const value = new ExecutionContextContainer(context).toString(); + expect(value).toBe( + 'test%E2%98%BAtype:test%E6%BC%A2name:Visualization%E2%98%BA%E6%BC%A2%E5%AD%97' + ); + }); + + it('returns an escaped string representation of provided execution context parent', () => { + const parentContext: KibanaExecutionContext = { + id: 'Dashboard☺漢字', + type: 'test☺type', + name: 'test漢name', + description: 'parent-descripton', + }; + const parentContainer = new ExecutionContextContainer(parentContext); + + const context: KibanaExecutionContext = { + id: 'Visualization', type: 'test-type', name: 'test-name', description: 'test-description', }; - const value = new ExecutionContextContainer(context).toString(); - expect(value).toBe('test-type:test-name:Visualization%E2%98%BA%E6%BC%A2%E5%AD%97'); + const value = new ExecutionContextContainer(context, parentContainer).toString(); + expect(value).toBe( + 'test%E2%98%BAtype:test%E6%BC%A2name:Dashboard%E2%98%BA%E6%BC%A2%E5%AD%97;test-type:test-name:Visualization' + ); }); it('trims a string representation of provided execution context if it is bigger max allowed size', () => { diff --git a/src/core/server/execution_context/execution_context_container.ts b/src/core/server/execution_context/execution_context_container.ts index 066248a26ad7b..de895bcff5ecb 100644 --- a/src/core/server/execution_context/execution_context_container.ts +++ b/src/core/server/execution_context/execution_context_container.ts @@ -50,18 +50,23 @@ export interface IExecutionContextContainer { } function stringify(ctx: KibanaExecutionContext): string { - const stringifiedCtx = `${ctx.type}:${ctx.name}:${encodeURIComponent(ctx.id!)}`; + const stringifiedCtx = `${encodeURIComponent(ctx.type)}:${encodeURIComponent( + ctx.name + )}:${encodeURIComponent(ctx.id!)}`; return ctx.child ? `${stringifiedCtx};${stringify(ctx.child)}` : stringifiedCtx; } export class ExecutionContextContainer implements IExecutionContextContainer { readonly #context: Readonly; + constructor(context: KibanaExecutionContext, parent?: IExecutionContextContainer) { this.#context = parent ? { ...parent.toJSON(), child: context } : context; } + toString(): string { return enforceMaxLength(stringify(this.#context)); } + toJSON() { return this.#context; } diff --git a/src/core/server/execution_context/integration_tests/tracing.test.ts b/src/core/server/execution_context/integration_tests/tracing.test.ts index d329b2216f2ec..f1a7ce8c9b46b 100644 --- a/src/core/server/execution_context/integration_tests/tracing.test.ts +++ b/src/core/server/execution_context/integration_tests/tracing.test.ts @@ -18,6 +18,13 @@ const parentContext = { description: 'test-description', }; +const withUtf8CharsContext = { + type: 'test字type', + name: 'test漢字name', + id: '9000☺', + description: 'test-description', +}; + describe('trace', () => { let esServer: kbnTestServer.TestElasticsearchUtils; let root: ReturnType; @@ -362,6 +369,35 @@ describe('trace', () => { expect(response.body).toEqual(parentContext); }); + it('supports UTF-8 characters', async () => { + const { http } = await root.setup(); + const { createRouter } = http; + + const router = createRouter(''); + router.get({ path: '/execution-context', validate: false }, async (context, req, res) => { + const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping( + {}, + { meta: true } + ); + return res.ok({ body: headers || {} }); + }); + + await root.start(); + const response = await kbnTestServer.request + .get(root, '/execution-context') + .set('x-opaque-id', 'utf-test') + .set(new ExecutionContextContainer(withUtf8CharsContext).toHeader()) + .expect(200); + + const rawOpaqueId = response.body['x-opaque-id']; + expect(rawOpaqueId).toEqual( + 'utf-test;kibana:test%E5%AD%97type:test%E6%BC%A2%E5%AD%97name:9000%E2%98%BA' + ); + expect(decodeURIComponent(rawOpaqueId)).toEqual( + 'utf-test;kibana:test字type:test漢字name:9000☺' + ); + }); + it('execution context is the same for all the lifecycle events', async () => { const { executionContext, http } = await root.setup(); const { diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx index e16368f522952..884a26a85e6d0 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.tsx @@ -394,7 +394,7 @@ export class VisualizeEmbeddable const parentContext = this.parent?.getInput().executionContext; const child: KibanaExecutionContext = { type: 'visualization', - name: this.vis.type.title, + name: this.vis.type.name, id: this.vis.id ?? 'an_unsaved_vis', description: this.vis.title || this.input.title || this.vis.type.name, url: this.output.editUrl, diff --git a/x-pack/test/functional_execution_context/tests/browser.ts b/x-pack/test/functional_execution_context/tests/browser.ts index e2578934cb00b..aa990dccda86c 100644 --- a/x-pack/test/functional_execution_context/tests/browser.ts +++ b/x-pack/test/functional_execution_context/tests/browser.ts @@ -252,7 +252,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { predicate: (record) => Boolean( record.http?.request?.id?.includes( - 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:TSVB:bcb63b50-4c89-11e8-b3d7-01146121b73d' + 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:metrics:bcb63b50-4c89-11e8-b3d7-01146121b73d' ) ), retry, @@ -269,7 +269,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { url: '/view/7adfa750-4c81-11e8-b3d7-01146121b73d', child: { type: 'visualization', - name: 'TSVB', + name: 'metrics', id: 'bcb63b50-4c89-11e8-b3d7-01146121b73d', description: '[Flights] Delays & Cancellations', url: '/app/visualize#/edit/bcb63b50-4c89-11e8-b3d7-01146121b73d', @@ -279,13 +279,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); + // application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:vega:ed78a660-53a0-11e8-acbd-0be0ad9d822b it('propagates context for Vega visualizations', async () => { await assertLogContains({ description: 'execution context propagates to Elasticsearch via "x-opaque-id" header', predicate: (record) => Boolean( record.http?.request?.id?.includes( - 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:Vega:ed78a660-53a0-11e8-acbd-0be0ad9d822b' + 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:vega:ed78a660-53a0-11e8-acbd-0be0ad9d822b' ) ), retry, @@ -302,7 +303,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { url: '/view/7adfa750-4c81-11e8-b3d7-01146121b73d', child: { type: 'visualization', - name: 'Vega', + name: 'vega', id: 'ed78a660-53a0-11e8-acbd-0be0ad9d822b', description: '[Flights] Airport Connections (Hover Over Airport)', url: '/app/visualize#/edit/ed78a660-53a0-11e8-acbd-0be0ad9d822b', @@ -318,7 +319,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { predicate: (record) => Boolean( record.http?.request?.id?.includes( - 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:Tag cloud:293b5a30-4c8f-11e8-b3d7-01146121b73d' + 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:tagcloud:293b5a30-4c8f-11e8-b3d7-01146121b73d' ) ), retry, @@ -335,7 +336,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { url: '/view/7adfa750-4c81-11e8-b3d7-01146121b73d', child: { type: 'visualization', - name: 'Tag cloud', + name: 'tagcloud', id: '293b5a30-4c8f-11e8-b3d7-01146121b73d', description: '[Flights] Destination Weather', url: '/app/visualize#/edit/293b5a30-4c8f-11e8-b3d7-01146121b73d', @@ -351,7 +352,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { predicate: (record) => Boolean( record.http?.request?.id?.includes( - 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:Vertical bar:9886b410-4c8b-11e8-b3d7-01146121b73d' + 'kibana:application:dashboard:7adfa750-4c81-11e8-b3d7-01146121b73d;visualization:histogram:9886b410-4c8b-11e8-b3d7-01146121b73d' ) ), retry, @@ -368,7 +369,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { url: '/view/7adfa750-4c81-11e8-b3d7-01146121b73d', child: { type: 'visualization', - name: 'Vertical bar', + name: 'histogram', id: '9886b410-4c8b-11e8-b3d7-01146121b73d', description: '[Flights] Delay Buckets', url: '/app/visualize#/edit/9886b410-4c8b-11e8-b3d7-01146121b73d', diff --git a/x-pack/test/functional_execution_context/tests/server.ts b/x-pack/test/functional_execution_context/tests/server.ts index fd10118a03627..adb92fc6a2283 100644 --- a/x-pack/test/functional_execution_context/tests/server.ts +++ b/x-pack/test/functional_execution_context/tests/server.ts @@ -73,7 +73,7 @@ export default function ({ getService }: FtrProviderContext) { Boolean( // exclude part with taskId record.http?.request?.id?.includes( - `kibana:task manager:run alerting:test.executionContext:` + `kibana:task%20manager:run%20alerting%3Atest.executionContext:` ) ), retry, @@ -84,7 +84,7 @@ export default function ({ getService }: FtrProviderContext) { 'alerting execution context propagates to Elasticsearch via "x-opaque-id" header', predicate: (record) => Boolean( - record.http?.request?.id?.includes(`alert:execute test.executionContext:${alertId}`) + record.http?.request?.id?.includes(`alert:execute%20test.executionContext:${alertId}`) ), retry, });