From 5c9df67e8daa197fe927333b6ca23520038b1a02 Mon Sep 17 00:00:00 2001 From: Kevin Lacabane Date: Tue, 3 Jan 2023 16:20:17 +0100 Subject: [PATCH 01/30] [Stack Monitoring] Beats package integration tests (#147755) ### Summary **Sets up the foundations for https://github.com/elastic/kibana/issues/146000** - created a new test server under `x-pack/test/monitoring_api_integration/` that allows loading of packages at kibana startup - a test runner utility which is a simple for loop executing the supplied test twice, one time with `metricbeat` data and a second time with `package` data - a utility that allows transformation of package data into metricbeat data **Adds API tests for the beats package** - created a test case for each API exposed - removed the duplicates from `x-pack/test/api_integration/apis/monitoring` ----- _See the included [README](https://github.com/elastic/kibana/blob/b55de5c1ccf2d654bd03cf5f856765fdc85c82f6/x-pack/test/monitoring_api_integration/README.md) for additional details_ This directory defines a custom test server that provides bundled integrations packages to the spawned test Kibana. This allows us to install those packages at startup, with all their assets (index templates, ingest pipelines..), without having to reach a remote package registry. With the packages and their templates already installed we don't have to provide the static mappings in the tests archives. This has the benefit of reducing our disk footprint and setup time but more importantly it enables an easy upgrade path of the mappings so we can verify no breaking changes were introduced by bundling the new versions of the packages. _Note that while Stack Monitoring currently supports 3 collection modes, the tests in this directory only focus on metricbeat and elastic-agent data. Tests for legacy data are defined under `x-pack/test/api_integration/apis/monitoring`._ Since an elastic-agent integration spawns the corresponding metricbeat module under the hood (ie when an agent policy defines elasticsearch metrics data streams, a metricbeat process with the elasticsearch module will be spawned), the output documents are _almost_ identical. This means that we can easily transform documents from a source (elastic-agent) to another (metricbeat), and have the same tests run against both datasets. Note that we don't have to install anything for the metricbeat data since the mappings are already installed by elasticseach at startup, and available at `.monitoring--8-mb` patterns. So we are always running the metricbeat tests against the latest version of the mappings. We could have a similar approach for packages, for example by installing the latest packages versions from public EPR before the test suites run, instead of using pinned versions. Besides the questionable reliance on remote services for running tests, this is also dangerous given that packages are released in a continuous model. This means that whenever the test suite would execute against the latest version of packages it would be too late, as in already available to users. Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .buildkite/ftr_configs.yml | 1 + .../apis/monitoring/apm/index.js | 3 - .../apis/monitoring/apm/instance_mb.js | 46 --------- .../apis/monitoring/apm/instances_mb.js | 80 ---------------- .../apis/monitoring/apm/overview_mb.js | 44 --------- .../apis/monitoring/beats/detail_mb.js | 44 --------- .../apis/monitoring/beats/index.js | 3 - .../apis/monitoring/beats/list_mb.js | 77 ---------------- .../apis/monitoring/beats/overview_mb.js | 41 --------- .../monitoring/apm_mb/data.json.gz | Bin 5108 -> 0 bytes .../monitoring/beats_mb/data.json.gz | Bin 643153 -> 0 bytes .../data.json.gz | Bin 187819 -> 0 bytes .../test/monitoring_api_integration/README.md | 53 +++++++++++ .../apis/apm/index.ts | 16 ++++ .../apis/apm/instance.ts | 41 +++++++++ .../apis/apm/instances.ts | 39 ++++++++ .../apis/apm/overview.ts | 39 ++++++++ .../apis/beats/beat.ts | 42 +++++++++ .../apis/beats/beats.ts | 40 ++++++++ .../apis/beats/index.ts | 16 ++++ .../apis/beats/overview.ts | 46 +++++++++ .../monitoring_api_integration/apis/index.ts | 15 +++ .../archives/apm/metricbeat/data.json.gz | Bin 0 -> 14823 bytes .../archives/apm/package/data.json.gz | Bin 0 -> 12157 bytes .../archives/beats/metricbeat/data.json.gz | Bin 0 -> 8902 bytes .../archives/beats/package/data.json.gz | Bin 0 -> 7743 bytes .../test/monitoring_api_integration/config.ts | 32 +++++++ .../fixtures/apm/instance.json | 1 + .../fixtures/apm/instances.json | 1 + .../fixtures/apm/overview.json | 1 + .../fixtures/beats/beat.json | 1 + .../fixtures/beats/beats.json | 1 + .../fixtures/beats/overview.json | 1 + .../fixtures/packages/beat-0.0.1.zip | Bin 0 -> 121912 bytes .../monitoring_api_integration/packages.ts | 21 +++++ .../scripts/transform_archive.js | 87 ++++++++++++++++++ .../utils/lifecycle_methods.ts | 39 ++++++++ .../utils/test_runner.ts | 41 +++++++++ x-pack/test/tsconfig.json | 2 + 39 files changed, 576 insertions(+), 338 deletions(-) delete mode 100644 x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js delete mode 100644 x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js delete mode 100644 x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js delete mode 100644 x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js delete mode 100644 x-pack/test/api_integration/apis/monitoring/beats/list_mb.js delete mode 100644 x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js delete mode 100644 x-pack/test/functional/es_archives/monitoring/apm_mb/data.json.gz delete mode 100644 x-pack/test/functional/es_archives/monitoring/beats_mb/data.json.gz delete mode 100644 x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb/data.json.gz create mode 100644 x-pack/test/monitoring_api_integration/README.md create mode 100644 x-pack/test/monitoring_api_integration/apis/apm/index.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/apm/instance.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/apm/instances.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/apm/overview.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/beats/beat.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/beats/beats.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/beats/index.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/beats/overview.ts create mode 100644 x-pack/test/monitoring_api_integration/apis/index.ts create mode 100644 x-pack/test/monitoring_api_integration/archives/apm/metricbeat/data.json.gz create mode 100644 x-pack/test/monitoring_api_integration/archives/apm/package/data.json.gz create mode 100644 x-pack/test/monitoring_api_integration/archives/beats/metricbeat/data.json.gz create mode 100644 x-pack/test/monitoring_api_integration/archives/beats/package/data.json.gz create mode 100644 x-pack/test/monitoring_api_integration/config.ts create mode 100644 x-pack/test/monitoring_api_integration/fixtures/apm/instance.json create mode 100644 x-pack/test/monitoring_api_integration/fixtures/apm/instances.json create mode 100644 x-pack/test/monitoring_api_integration/fixtures/apm/overview.json create mode 100644 x-pack/test/monitoring_api_integration/fixtures/beats/beat.json create mode 100644 x-pack/test/monitoring_api_integration/fixtures/beats/beats.json create mode 100644 x-pack/test/monitoring_api_integration/fixtures/beats/overview.json create mode 100644 x-pack/test/monitoring_api_integration/fixtures/packages/beat-0.0.1.zip create mode 100644 x-pack/test/monitoring_api_integration/packages.ts create mode 100644 x-pack/test/monitoring_api_integration/scripts/transform_archive.js create mode 100644 x-pack/test/monitoring_api_integration/utils/lifecycle_methods.ts create mode 100644 x-pack/test/monitoring_api_integration/utils/test_runner.ts diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index aebbfa30dc47..d98f9c77f216 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -223,6 +223,7 @@ enabled: - x-pack/test/licensing_plugin/config.public.ts - x-pack/test/licensing_plugin/config.ts - x-pack/test/lists_api_integration/security_and_spaces/config.ts + - x-pack/test/monitoring_api_integration/config.ts - x-pack/test/observability_api_integration/basic/config.ts - x-pack/test/observability_api_integration/trial/config.ts - x-pack/test/observability_functional/with_rac_write.config.ts diff --git a/x-pack/test/api_integration/apis/monitoring/apm/index.js b/x-pack/test/api_integration/apis/monitoring/apm/index.js index ae0623054dc6..e28a3bdcaf61 100644 --- a/x-pack/test/api_integration/apis/monitoring/apm/index.js +++ b/x-pack/test/api_integration/apis/monitoring/apm/index.js @@ -8,10 +8,7 @@ export default function ({ loadTestFile }) { describe('APM', () => { loadTestFile(require.resolve('./overview')); - loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./instances')); - loadTestFile(require.resolve('./instances_mb')); loadTestFile(require.resolve('./instance')); - loadTestFile(require.resolve('./instance_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js b/x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js deleted file mode 100644 index 966a4850c216..000000000000 --- a/x-pack/test/api_integration/apis/monitoring/apm/instance_mb.js +++ /dev/null @@ -1,46 +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 apmInstanceFixture from './fixtures/instance.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - - describe('instance detail mb', function () { - // Archive contains non-cgroup data which collides with the in-cgroup services present by default on cloud deployments - this.tags(['skipCloud']); - - const { setup, tearDown } = getLifecycleMethods(getService); - const archive = 'x-pack/test/functional/es_archives/monitoring/apm_mb'; - const timeRange = { - min: '2018-08-31T12:59:49.104Z', - max: '2018-08-31T13:59:49.104Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(); - }); - - it('should get apm instance data', async () => { - const { body } = await supertest - .post( - '/api/monitoring/v1/clusters/GUtE4UwgSR-XUICRDEFKkA/apm/9b16f434-2092-4983-a401-80a2b61c79d6' - ) - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - expect(body).to.eql(apmInstanceFixture); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js b/x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js deleted file mode 100644 index 69c194656961..000000000000 --- a/x-pack/test/api_integration/apis/monitoring/apm/instances_mb.js +++ /dev/null @@ -1,80 +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 { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - - describe('list mb', function () { - // Archive contains non-cgroup data which collides with the in-cgroup services present by default on cloud deployments - this.tags(['skipCloud']); - - const { setup, tearDown } = getLifecycleMethods(getService); - const archive = 'x-pack/test/functional/es_archives/monitoring/apm_mb'; - const timeRange = { - min: '2018-08-31T12:59:49.104Z', - max: '2018-08-31T13:59:49.104Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(); - }); - - it('should load multiple clusters', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/GUtE4UwgSR-XUICRDEFKkA/apm/instances') - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - const expected = { - stats: { - totalEvents: 18, - apms: { - total: 2, - }, - timeOfLastEvent: '2018-08-31T13:59:21.199Z', - }, - apms: [ - { - uuid: '55f1089b-43b1-472a-919a-344667bae595', - name: 'd06490170f2b', - type: 'Apm-server', - output: 'Elasticsearch', - total_events_rate: 0.0033333333333333335, - bytes_sent_rate: 5.7316666666666665, - errors: 0, - memory: 3445920, - version: '7.0.0-alpha1', - time_of_last_event: '2018-08-31T13:59:21.199Z', - }, - { - uuid: '9b16f434-2092-4983-a401-80a2b61c79d6', - name: '01323afae1fb', - type: 'Apm-server', - output: 'Elasticsearch', - total_events_rate: 0.0016666666666666668, - bytes_sent_rate: 2.9105555555555553, - errors: 0, - memory: 3087640, - version: '7.0.0-alpha1', - time_of_last_event: '2018-08-31T13:59:21.163Z', - }, - ], - cgroup: false, - }; - - expect(body).to.eql(expected); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js b/x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js deleted file mode 100644 index 2c56416fb230..000000000000 --- a/x-pack/test/api_integration/apis/monitoring/apm/overview_mb.js +++ /dev/null @@ -1,44 +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 apmClusterFixture from './fixtures/cluster.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - - describe('overview mb', function () { - // Archive contains non-cgroup data which collides with the in-cgroup APM server present by default on cloud deployments - this.tags(['skipCloud']); - const { setup, tearDown } = getLifecycleMethods(getService); - - const archive = 'x-pack/test/functional/es_archives/monitoring/apm_mb'; - const timeRange = { - min: '2018-08-31T12:59:49.104Z', - max: '2018-08-31T13:59:49.104Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(); - }); - - it('should summarize apm cluster with metrics', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/GUtE4UwgSR-XUICRDEFKkA/apm') - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - expect(body).to.eql(apmClusterFixture); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js b/x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js deleted file mode 100644 index 7bad06ac7df1..000000000000 --- a/x-pack/test/api_integration/apis/monitoring/beats/detail_mb.js +++ /dev/null @@ -1,44 +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 beatDetailFixture from './fixtures/detail.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - - describe('instance detail mb', () => { - const { setup, tearDown } = getLifecycleMethods(getService); - const archive = - 'x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb'; - const timeRange = { - min: '2018-02-09T20:49:00Z', - max: '2018-02-09T21:50:00Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(); - }); - - it('should summarize beat with metrics', async () => { - const { body } = await supertest - .post( - '/api/monitoring/v1/clusters/fHJwISmKTFO8bj57oFBLUQ/beats/beat/60599a4f-8139-4251-b0b9-15866df34221' - ) - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - expect(body).to.eql(beatDetailFixture); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/beats/index.js b/x-pack/test/api_integration/apis/monitoring/beats/index.js index 8fe09e457f68..f3156ae7bce6 100644 --- a/x-pack/test/api_integration/apis/monitoring/beats/index.js +++ b/x-pack/test/api_integration/apis/monitoring/beats/index.js @@ -8,10 +8,7 @@ export default function ({ loadTestFile }) { describe('Beats', () => { loadTestFile(require.resolve('./overview')); - loadTestFile(require.resolve('./overview_mb')); loadTestFile(require.resolve('./list')); - loadTestFile(require.resolve('./list_mb')); loadTestFile(require.resolve('./detail')); - loadTestFile(require.resolve('./detail_mb')); }); } diff --git a/x-pack/test/api_integration/apis/monitoring/beats/list_mb.js b/x-pack/test/api_integration/apis/monitoring/beats/list_mb.js deleted file mode 100644 index fdc0184d9e8e..000000000000 --- a/x-pack/test/api_integration/apis/monitoring/beats/list_mb.js +++ /dev/null @@ -1,77 +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 { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - - describe('list mb', () => { - const { setup, tearDown } = getLifecycleMethods(getService); - describe('with restarted beat instance', () => { - const archive = - 'x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb'; - const timeRange = { - min: '2018-02-09T20:49:00Z', - max: '2018-02-09T21:50:00Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(); - }); - - it('should load multiple clusters', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/fHJwISmKTFO8bj57oFBLUQ/beats/beats') - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - const expected = { - stats: { - total: 2, - types: [ - { type: 'Metricbeat', count: 1 }, - { type: 'Filebeat', count: 1 }, - ], - stats: { totalEvents: 12829, bytesSent: 2040312125 }, - }, - listing: [ - { - uuid: '2736e08b-5830-409b-8169-32aac39c5e55', - name: 'spicy.local', - type: 'Filebeat', - output: 'Elasticsearch', - total_events_rate: 0.018032786885245903, - bytes_sent_rate: 24135.450546448086, - errors: 0, - memory: 30680648, - version: '7.0.0-alpha1', - }, - { - uuid: '60599a4f-8139-4251-b0b9-15866df34221', - name: 'spicy.local', - type: 'Metricbeat', - output: 'Elasticsearch', - total_events_rate: 3.1327868852459018, - bytes_sent_rate: 2613.3185792349727, - errors: 0, - memory: 7598304, - version: '7.0.0-alpha1', - }, - ], - }; - expect(body).to.eql(expected); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js b/x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js deleted file mode 100644 index 9be0d00a34f0..000000000000 --- a/x-pack/test/api_integration/apis/monitoring/beats/overview_mb.js +++ /dev/null @@ -1,41 +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 beatsClusterFixture from './fixtures/cluster.json'; -import { getLifecycleMethods } from '../data_stream'; - -export default function ({ getService }) { - const supertest = getService('supertest'); - - describe('overview mb', () => { - const { setup, tearDown } = getLifecycleMethods(getService); - const archive = 'x-pack/test/functional/es_archives/monitoring/beats_mb'; - const timeRange = { - min: '2017-12-19T18:11:32.000Z', - max: '2017-12-19T18:14:38.000Z', - }; - - before('load archive', () => { - return setup(archive); - }); - - after('unload archive', () => { - return tearDown(); - }); - - it('should summarize beats cluster with metrics', async () => { - const { body } = await supertest - .post('/api/monitoring/v1/clusters/FlV4ckTxQ0a78hmBkzzc9A/beats') - .set('kbn-xsrf', 'xxx') - .send({ timeRange }) - .expect(200); - - expect(body).to.eql(beatsClusterFixture); - }); - }); -} diff --git a/x-pack/test/functional/es_archives/monitoring/apm_mb/data.json.gz b/x-pack/test/functional/es_archives/monitoring/apm_mb/data.json.gz deleted file mode 100644 index 226e2f8f76f66bef3369f980871518f8d6ec8b35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5108 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

Gc$8!a`hLMk=}R| z@}z>U$bPg@-TBXG-^eAR#j-7;TQtoS@febzbo(vBElFAWgnZK&?k6fFC81mzv#!?2T=^F@f0JJ9 z)x=k<_Es2)RPZ+0OYEKw+fJEFY7)za>=feFwl&898zOi4bxSH|L>|vz4v|qN^7vqd zDU@*iRpbOxbSl@rqPYV=vGiMQlm2T)oQ}IvTmWy0JS`q&F8o0rk*17`m;E6q4FycW zykqG9;vKO7-cd|t*GPY$(@z1(Z8yo=v#!lEj-dgT`(5<0Ljk57OQ;EUNeK*QQH4q`PK7>COS^?v`!= z>F!WKdWP5_id;P3ap@B7)uKK4G2{f;-Rxz<|W^}Vi{>s;rD zj3Dz%JoU01hQk6i)hROhEarp_85HDaUc9{U81L1k{qbvKMx@TVO|0#V%rd_kBzd3S zL4^c}v!NnMQveZ(V#f11&CK_{QK!BJ8ej6sDk3!h)-uH!}f1m%d}8DOrMoCQACHL!PKi zF=gX5&W(f_aY}0D{kZR$x)iQ}HD0-l9KYuRKq4y5^+m@wcwh3W3O}xXq#LZXC;?=M z>cr>dR=wf^3%SEMoc^&cZogLSqE5KRSNTz;{f(2<~W}JVo)f>fvV1ph?7|zZJ=^rPFv(AC|WY6(C9|DN}dP@pEVfGw(C^Qrb zND{-LIJ=1_CI(vOb9C?8G*{f@i6Ng3|7h(ZaH|Tf?m-8D#wTp|oMNfVnY}mioE-*G+}6sNy$DqUQ?Ls9rjvhXJjvma5|~ ziR)Ms3rjRCE5w`mH=m}`B`T>sOd63Ya!x7RMlRY6^LAjG=_) zIi1tEU5*MVX9e;4GEf0gj6_~C>pG7dg9#C2kz@g;|BW14U#r^kRRMK+0nRE9m;%%L*J^)CD4&SoPKPX%T@PBbPqG-vjF#FyuShtk=D{>QO< z?*?;$HM#~N0}CSvJTb?T3}G9ObdI+-41 zs3DG9ig0z{yJe={Xsi!8o-RX;jQpZKusEgPeJyHosd2orR8OILuoxoJTu2%kq`yQu6oGiyXMY;LQw9??9@wtx4IKiD;K_HjZk0It!sCrOO94Y^`8awP77%FGDL;AF?V{$~UpQwHj2X;U}RB81cW zR*Tj2B4JNNA!+#G4q~|ma_p`I72XX9c=pDYi)cvk&bvA^3hzTbc(g==qvAUGu-aVU*@tyL-#bud-uvWqRQKoWF`uH}b_E-*xdqVj%jfjnU zPPz&lLX(^cNuj1wjQ$cg3z8kjQ2E-~#n)DriZLW36jKUtj^pwgo&R!U+KwcH-lHb=4@oP+B+gg*h*T?~6$Ly(*dGlXL0D#{-&{nhPe zBXjQ_I~qQ{^?!ah*w=@s66lj5M9Iro$k#ZcoWkJoi|n(ttV*ujK{zLaOdS!o-Q9xq~dlex_SykzLG;X7Mu1mEDa+k0g+_ zKU&#HJvud%4?OSNE4%)F0$Dv&s+yo^d=-)6jo(iUmqPZ0Q#`#J{pqSkIU}DZdEVlo z{DM`0Ouznc>BtEM`tFM#bKCh1_aI+cq_)F{tB&QRV625veXnh6$zGeKR^t!*0eepK zgP}vwkcqfQ41b-FH!CmYI9-c{?U$O;ekZG%=9rdq+nfC~d9s1)+Om|&*H0A?{G-RY zc+r8%JDey{*a}wsJHnJGV8=tx+@)6K1yf6x4A*Ha?dk9HueS!I@z)XAKV9_?Z$Q?Z$zwIkD#CU;8+(tKH4HM&c`{Pa5A&PA|UH(SQGn}^(9-%Wo} zU5j7YcIBc^VED!LarmO=u6I7lE|VW^PB_Sy_wZDJ#%e2ndgCycCf6`xmong+pNn5| zkWVA0F}iwH0H*Sj*T8$ybqm+~cpuxRH#!Y3-b+e&4d;9ON%ZLF_~2ju?zcAQwGl;r zm41Bo2FGJoN!;$NrqGtWhMlrnr0kjAH*W3rO_w(VNeY zsY~YYOEyfT{2h<;LddoN72pR}6eDuyi=6`J#}Ps2AE$oXcEkqRCfU&RsflA;>v?#2=PRT52SC!} zPqB@6k`)|~bPF1%fVFM#=Pexdz8v!9A<7G`o zkS%Eg&ahhKO2$AOJ?dCl-r3=|s&o%@KnTflvg=PDn}fMl!S2ryXN*4bO0)R;<^9Ko zrJSHqX@HPilbqfbdAF^7KrJ^S$Qkm|V4$ux@zqSb7}?WE9%bq+-fwG}_4RdUFAmJF zXD;%me+>4_OVDK9iM83BG){?0f_^K3&Xtfp%2*-?ifAukvs^Ueg-nlR{h3(2;6+Fw zM~JbT07o$SmC3r8_%o*vliB!z$RA5}j~`FBe77FwbAez^LIM>*_tv#X{r&c<`xFsQ zqWd#(OyQ32$O$&D&Kdjv;3`icCv~lu3hdo)b7M+f4qjs_dCxS3ymPKWN=HZ^N>HXT z^1069pFO_qeY#A@1#9WWc$Gr;DsE-%>YGEruhXDEbZgux7_U%QveM_Dqbm4z{HrkfHdN_y|N)SqLL53(N&BYV!TJ ziG0z5?HV~*sBAE+crv<+I1H5mks)(I*pA`yb5+Q>UgMvJ@hfB*!t(i^G_RZ=X$oO< zdq1cIeL*M{f8og9CO2)Yk^8hKF1(ujL<4=S-|7q%dDgpE42*EG=!mOcy*L&iGW~9g zBtaEQyjxNE3U+qg^SMR-LX?$#5CQBajdezIT>M7!S?u==Oy#X^9PnC`+cPEII0h?o z;?4n{*yXfERI=`xfYiW(n1NbxL;yqNk(47?)QxdH2{$!q3e#MDa6wW(kIT{&DvTzz zhPlo6k(Y*gJJKrZIs_!{BphQ z-5fZwK;~jrQ>oj~O_kj@lIbY|>@#e$-GA>bx<4jHhL)x;kpm}RzB zwXhP7mX1U`axp^jP@-m2R{E+uWkvAX3^$ijPMmamS@c+UGe!X*Z5~q2NC%9+a>eus46eTQUu0l=B(#iJR+dWeH zgq;wn5TS4=Q9}gp_I#u4u2QY(GeTjPA_&ZSe)!w0tupSBCe3`$Fz7PC$4!o&n+vP# zV`Qa@1*{x4xs$@$B6kKogbCcHR8*5ZuqyyB`v zQE1#;Y&iT?MJ;SC8p>W(@RJU6E5%gUk3BHsef&DJw4MApv7??cik@4}shFRE1s2RY zOb%j-wZ;*?+Vn7V)Ala^`FzJ>rpWe>cjSIP` ztfUT%$aS(A91@0ui$(u~M6M71i$qeqC?@&2m-p*@d3Vp;6}OQG%kW*A9t7up%R|K4}XH!a7QPn*`U!qMJ(=~0iyM`(xk zzI8FaMgns}{;Aei)Z?8_L!32Rs8)Eb6yeEOSdJB&m_3gcW$%J6G{ZXX))}1O$ZarKPF9S;)+5qEFiOLJ84fOruOCWGSIGD8BHD z8D?zmkjP)Tl_bUU`Gg~e1SlZ6krJPvCBhVb1+BvskURe#8nSMmRnERLz9RP7m~ zA6JpV>4qvqJLK+n+1V>B=`rYX3x)X-!fC(CP3Ry|Yhpsp%RkMuILlD-zPisZB?`43 z6$XCu;fv2!NAli>0W2h89kUg{LdN`ug`5Of$h7-)xUta#88)K-WFIXT@!C5WKoTQD zXRr{uC;O>@-#fGHWz}-Y$u*KRY#Dk_4yjew(1@?Jj+t#BE0xu#vW6>Qz<@R(?&n1q zjx{}v3_*m{M5*R9$RG|$HNctJ~a)nx8U1`5^mbo6689Lx$1Bzon+xv97=cv2#rX}t?Oq~HI z`HT(r)UdmIZI!5!xabppzDiclFslvmbYP(L&L%YZN{r1)5}1-_h7cv(`aZM#3qk^Q ze<*W1-+p_JI)kW!(3u9Ht{7%+I zWc^M8e34Z&j zG>%K#wUJlc%72 zk!DBvJm~n4m`h~xUkK9gPePd+vJy~Q%m4&wvPBO-kXjcA5_QNLDd~_gxW2E4Z#v!l z=J2RwzAfp+);j01Vh%2ic61<+`w>j&bC*!^tS+7*@OW0h1p?k!#r(C9ZTI3S9Dz&$ z5J(7?%|{}$4?Cwg;6`(12pzyJ?pCmkt~)Fge5*Pemh8Bo4b@p4TIa@lH8#bn6(bB~ zEyhzLIZ0TW5^&MNEU_q)Wnu)x6|11ndD?aKBQMqUf8^793h0U7SU*@F>GeljY}P%sWcfRVenlGC z_(gb1hpCl`9Hp){uz>kRIzb_+aCUYGPhH~P#rv1(8fTkv2fA2{jywlT6Sd?;Iu1$M{^t45>s`Y&_@Xd@z(jw77mNM)e`vEQ@k@L;VXl`J?~awH;{HYl<1mRpJ`5&OXsV`H4PZ zn=z1V1~yfKd>sxd1f15vbScuSA&XDfozwG!ZX+-M?so3dZPNA^J{_}KVq(Qx{*PBJ zyxSS5^DVYyb#W3{76VpmeNr2xMuXW3veT+;-*!a9-xHSbeEg=SgQitiuFOm;@0 z+)V$6cyt7aM`AefSXL*X*GjUNdVZcivT;r$6vE(j$282Q#lseTfS41%W=mosXDfiI zS64L{X%OYur;xYPh5lihiMf{zw6q9k#dvx{f8g#38f|shuUX?5Fsrp;$(agY9!*6% zL~(@)>2J=R-=K{2Bu^+sMFr?!H-2p_U8N}?QA2@|t3ve{;tl$h@5i{y@2&qxMdH8JK>Dt|)?&!~KBye7dQADNdIV-Y{(;kf zRF6Oxs;-~A3;@f>fbf$o3|Z=F`;m6q50f07ojps&)1^ z>Cz-L$dw?+{&sW?SxiLKlyZ?H#j1>*j_pFM!O;iLVY*w#m&U{MyQ;Vimtw12m7y(Nk&~sP$>UF>>IV$90XUaFeG6@WB)g)F%G&xjtOK zn9_VnqQR_2tFijFCA`Am~%^BF- z`mWWQ+y#H~n}7U;jn_O?5UOOlvL3s`G8(FdrOOoe>9Iv-B<-3m!Khf#iwDc&T$R2U zub=yCB*9S~*3Z$fNjIjX$a@(^O5=`mxa856EUv!E%f0>c!v20v*c{WiIdNP&64P4M z6hePBlg=#HgX7VOO0Y;L6ZXUrEj5{3hDVQwMivWIEe}{y(4?#@-{PYbHUMZTuoz&^ z|I=nO;5#GnkL}v814LELQx%Z4w9??3NA2R1hihz0Vr1d=RS3{LzIOax z6aLN$Fr1ETsY=kHDw~s-lUPx_up+s=?M=^X`Kj5q1mLkb)hVQ+lpepTA)DyeC*Oy? z-A*t$zOl~Td8f3a-%fm#vetoX9;MJL0nMY7mv<%6%azaTQjt4151PkJ%IL0NC!++mMVud->dlQI7Mk51oFgm~TD6L&*U-I^o-}T*1w@`63S%)>JgJ(z$&ZlYPaJerQ z3(c&qF6fLNIn=sbV=nM|EqosR+=e?JfqYUBJZz3%dXA^#30m{H;TWq7GLHp=zul7G9$AcNIAc zdAD>w1+=%~zUc@!xN$cLw4B?_jINka7z*Oe(YmiKvdbxh+}C^C$qw;5-r!x5RIwQk z>@NXQkcK--4$AKJ)k*HRFycI`IpH=U4`=&}UWFF}!!1kY-tC(h!cJLWzGtm3I(BdT z9nM`AJZ85A5^yP+KchB8zV=AG*EF)-Y8**`3gXuKEIKFPP;~bSZbyqKFX8@U5#;?X=5_U6 z(+3hRtteA;Fn2O8Nq+y;IqeGLTZ`y{@Fmp^HRi;^;*YE~!Hg#%&m}1(GF>g5lPh}2 z^uwj@H?Ecx$=9JvKCIIoR=YvBO|ADX&BH~SNt|BKJOp&}uPv{har$fx%oU`GahO$B z-_E$A<2y3*ac;=Ct>YdRc?GRXsP-gm79y_7!o7~D)=jm>y>rSNKPcLTI|+{#I6UIU zM$`6hXvE~wm;tS0ITpD?Hb6J#skGKq#>Dq$85$_)X1)C-AJt4Py^0Meup;qhW5?B^$ z<{ht_2{A?AZ2K|=QbSwN3YshK{Lu9EsIo^;A$|Ol-I|}MDc-x_M=iT~Uo76#p8}{c(@tfN$7p;sz*`t3q9s)isy`}o9CncW7)JfIe|PKufNVS5x|tKgcPMWET5Ds{NibmA)+5@rexdW zOf~ts9zM7E4c}VH1v4*-s{zB@Qu@PlsX67$Zwy`CQc$5DDrwcto64#owV1ceXEZV+ zmG-=i@kq{P>e=N7g0a8cBzh;rNkfFD8$%yM48Lh(5Y%@#9%P(Iql5`dU* z&o(XAT)U`HX&ULh?bR*_cLe*LYJrNN4G$Z&>CTRGW*mdVOp0eg*%u@d)?vbP71FUN zE-1cJgp*fBKLJ=dvxKe6)*c;o$OK_1^6j8laam;?*9TlG$=Vp$Gd;pKMYKioA@iM_r;=e$g&c%)HWd#4pU6gT0p>3Vjp`#gEN= z4MlQw4>Y?HGSqYz8X5VAbqo$UKw(w3jU>8^jdcC}ova2v6);{GRkzSu3U`@*YH^?U?sN@R%R;j*$*XaJ}Pg zJ5gHWVpr=EFtr0%3dZEmV(RksL(|H@iwtYr?}JC~=`)X0Ca1sGXc=Sd{uoLr6erSf zN7Mj*Za>jT(S4ON^n~gx!LZ<_^|=(96n`6oezd#odIEyNXVE0=vkNsyy|pPW39bPa zKshdGN4*ifZt;n(_iyNcXDyH3dGp}{S<4AJMFR?2Rw_=l*Cq-)Ep|u>90&^1E!H#& z;{%WskE~IigJ@L3e@Vx{13Jvd{WE_Jj}bi5o&tY4$0-A*7-Y;BdIYOjGXUYZz!Szu z8p=qo#*@gXnEKf{tJdNo!Jq*5jrC^))y$;)-)6-%6iMsXjEY=}31(F(OciF(ufww{ zPAMsLj`F-$8Yyjr{kJ2|XR!|<%kSW3$D!a*zivgezt{E{dzC3795zMHlDf&K@p+G$ zTZ&pHrb=2#v4SoGK^sYX^Ws7cu67)Ftu#=|xKiqlIKZIN4=TpBazOn7{brQ3fy_ew zHQ_XGpn!+beno*ZA5Zty1rWE4`b8-^bBI@Sbs#$BFf{O|=xSJ`6$_XpE3}HzoY4H3 zpBoBbj<^8kXpgB&z#9{(Az-3Q%o{TR=pA(#YdLHQ-AuL5Rb59>l1vbiA~X66g6c)G z))}q^24_S9HEtXwO-cC!(JtaGPByXuk zegyv{Dr5{%(Dw`ua6Mj{5yKHl5dX)ySL5mN?2ZA)ajOTD8k&nk04XC zUs#oY^jt;g{o<~|BvK#lQ&|$lo$^H?-0w(-^0$KE#c@%5VW(KOp%T<;1z$WXvB(+# zIvZOM8ZqjUpeB34UNL;Sv?nN<*Tp}DwsVV`G^x&OkMDJtg{s(oX@XrmQ<>Py+M`$L zdJezFJphoL72M_#wRsdV30*@9UNP9tMrGqbYg1^f#H2(vW^uK;T9^$x>{;_JPlaYY ze;&wa+eExO?+0-uiPfUKU-=OYajkBZ7STZ}IR)3W1_n$r*2HpF*s3^M?WD*I$r@@U zf3bDVltaq)=v(5z%w~H$J0wAd^1&mK4X{1_A?a@UpghzrB22f*W+qI#itSQ_koyQB z7awD;W%%)$=Eb+=nkkCYd}aML5C=w@;L3F+7V zTkl9uVxN&_AaPpe(sH4pP=SHpjqK#ph|81bM{nSf)1QFIX+FLxE5)J;Mu$KSF7gE+ zbec4jnOLr)BS$&4;V^%safy9z2Q&8<9Vhwh|pES^VZkf*)XBujIZBX zh;u1o5+v9wNUAXGbCqv)T3_BcZD>i?;NJ?kQRZLqrGmkt%3TDN`hCWimV{23)P>X|w&6|4sD> zp8?t36oJI)gzD8SB{!l{yOiN!K6W$uWYEs{)oL|K1^3Oq&@64k?By~{nN|cDObe*Q znzh$aijoxW$agM^%HPas z2weBr(Q)w=#ys({LRqjp56GNOa)wlY;p&fZKEP)65LHsrBjpCmJU87x48{btB7iUn zpolQo)B^}vP;#4Kzf8g~_EO?>Xz%w_cCn5D16#$4AbL)qj}STG?+{j@ex)oB*u&t` zu~RXDfW%Q?dm+O?-qAZ6OrAQM?DqVx@=-@MOPfW_cNeAJ1ff0>VF1BUkJUW36mrXI@yUBHB*QZGwC5O|e0gd^MHDU8 zscnOGj9hoj1lIXcXC%%w3*unpBj^dV8kWQ5!KcVzGp-E6t-*desL(gAX6&b!7Ur5l z{!5R6SedMh#e_tC7p%KCuX)F9UsK}v%Ke$n$Jm(Ba)_1r44`sE$ch9Fh30DMP(&#T zWn{8kOcF+sgQ!f!UN~((&Y1T5rkK# zggA1#)S2c7kZ?UA-$#S;>~7*E|I7OJUn|eN@*F`8LapVv>{hasP#rc$>kR~!&!Q?h z@tMgQ6h>Rjipj!_*k<(%EHdr6q{>R_Z1VPI;%M?co<}Ma>3uEfg}9rZTCL z*(3cphR9}l!^kFBd+2(94qsEB-1vyif3aqCHSMoa z4H{sp5aQt(mJeYEwys&DHhcGc>S@>N4;HG7F511DwrzxLyYd}+PzeY<*8e2Y-u)IV zo@pMVYJ6#|eBQ@Wt6Doj*2wviBh4LMXkkx@!a6}*(s`|EY@ZKTv@@nRhr3J&8k_m1 zpL@zQhu`1Eh_REI8GYe2<9glyCC$k4mu93amWQ#Df z9wB^6%u|l%o-sP)o7JeQqaJ!e;ffp5IE3py^h$tFutwb|!oB%ZPon7E=~IfQMe&!p zfpj>eD~BL~i(lo{|XmoGBwb|Vo zoiM}m!Rtj}L|_C!nYc)E>zf~Pdrjn(+seHDPiCBXKYp7$M`v*B$0=81%0Nm+NWNi3 zVxa$XT=l8t-n+kF1Ya@7h0=J^&C!?vHN5eK$HHAO7P>dCZyA=E zQUNjMWB1xV?N1_oZRH>g3e~7)s{vjg@s-NP{NswCRb5_5p*6xW-BH}IyZiGf4@n^g z$hN*nqJ19sH`|(x(?x;2Ii6Ch(vp}Q$hOu?`#0O#b9)`gwhrFFG$%HPU2+fnczV_9 z-gI_7bIaD^&0qu1w*FsuM)&_Kp3(d88BKoB58@@JKLHHg#5w8Z5msuGopUQ?8Emi$ zfHH7bsfgC{#fDk7txCg+_ypoPfgK%iH-LZA$!<&t!Nazx$ZcqA14;Aw+UH&(7kBl8 zCSSyoJ&()KY>|fXzEp@Wc36lcObP`hfXXBco<2SkZojEs>$&6|9pXyS z*#a8_iDcQC?YUHi-*5aQAs`6ogb}uss~XhkezrD0a-d8H6T)QP=%UHHeK7-bqaU!E zZy{$6Ip7!pF`>zI>ZH4##_gLUp;(oRxN#B&BxqR?n*w|BWB^JEKwIP+fo+4qLDy4% z+U88r-@&C)v&vEh#QA8aNTy?IN}%HXG@N6(}j~ox_rWWNX6021X#+ z`UO1MTFpOCteOk;A=w(f8*_Yk-Se~A`@%KgjQzV(PGgFT0amvR)KVf}aiT>LRX=sf ziYEI5nGOceAYLS0l(}t`36PI24c_L(iYx_um;fbG^!whC`gS{*V0C2+707^yW;l-2 zl39=jM?M;Q;~&Hlzt%%+TJhV7xH=q_lRkszBH4u&9TNFmE zSD)JJF2B!RdNoUh>B-SoO^su{UFo1Ydn4!Op;(bLSD|l%!Fr3wz`-0ioXp`OLB3>h zxrd-gwdHqgC|*(^gaukbj{g)`S?PdsFV_ACG<|>j>D&e#;1c1AM>v;=XpfR!J;+-E zS3K@?v2Le}#S`nmPyr>Q-=&W3gu;o-S|rX$cJAI3+V?(CBeH!L1>^TV>F?2)KOl^{ z-=l3Ll@jEzHi_Wb)ej%E?r|@-zdb5mAA+)aUMy+uKMO||sUnn<2L6hhLhhA>V5w0;g0?P{e%h0{mK&)IACd%|JC4-;=UUUk zJ(h!WiX>^G3UF7IkH{xIoXDXrd}+|wte|(5#exuFlDR9}hx1*5DC~dKjs<%EsvRFk zDgkOo;{T`}b)gDtck`E5OS>Ig`ExW$7#O*&y%;@7_<`7@&l0X27r7avElV343DrFc ztOH+{&R}V*hNi4%MiM_Gd>@C92)~Pd>&IJHKV2?%7kh?n(S1=$Mu2moF~p_;Cx5^0 zV@&!kI)z#C9U&1RQAJXBN^IQhzP>GF%AmEw==#xv+YydFVkE-e!rhJ;fZI{|AGf1F z#fvopFSoIA(fMYxgW%_gi9=r`wEpH<10=vx0zDk)P+8oA+mQq!Khc6&s#3Vre% z?Inr5Z}d*;o?KOD(Vi#a!R-ioa65Xk0&d4E!0j0OuiH_VKc8xMdcUWCDQux506`)% zjB59KlZfpD{(vBWDYRTNnQy6KYD9wwjCtfJ98;r_A$3ly<4Q?tI2ssMm+J7;XdXex zrB~bih6VadC+{?2gaK?MQ+5PmZB`G@Gq)E-B*I>{)(|P_%owUsI3q`?@k_Zs9flZH zRsWdSCaN5vKuGOkeM%97&A=EB2!w?m%!z@X>~lC zy)A!baNCR*NC*U4>lZrpvvJUn=4+JVZ$$lpzX>4$E`GncMx-T}A2+$BNq*h`3%AV{ z_b}$!IlrGD5e_2gA0{TZ_+ZJX^620;|78cF`f6Mfzj9GxjYMiESVO!_LM&MZ%M=u0 z2OLnW0tmFmdI+?p%cCN8RbRyh z7@50B#~vY0SSJAJ0KT1@^}$F4R7PhsrAUM*O9gHK#wZU?2@HLr2ZvCB4Rtio@PVE4 z=j-j!-WNwW2+Sk@4^X2X`fdcL{_Vk@WQZPu1~3}_!3F?%_VdLequyyKz)-6o=lvrF zFDXRkCBb~cd^(Go6a0L0i;4h*{8>a$|G$CO6feF(NA1>oH>S#~&feUi{l8<4l`N@C zlFe7;ZCe)sd&Rl{Mab(n6k*|c7NcW1vflqILc9CZn$L&$Y#k(r42R@_Xwm3W0!jwJ z*u`Q~No3aLaK#PAZ~`Qea0Y=bduH#OBA-m$ohA-%?+zRRQT^r+pD>-( z)4M23(E9`m+YZ8M@a!-rC)?D)83 zx@f;$eo+9_1(#rT&VdkQC+b6sGB2MY6|?})y_Fq5(go{10a{1hG(iQp))BZdV2KwgT7cH^SYLPxPgQfh|GrnC>?tb0G1F~ zASMoKRfuhl8LF?)SZo@iKDq!Q)xdEXwuXMvf0T{@?4sr8@^Ss@$nb0 z2*=Pb%gG0$V-k1+UZ_Cc^2;}oI!|yC=_?uk7XS z@dvdBDtts>a}x;}$59(IA~XWE*)8z6D1f!m=SVXN73*l&F_{hYz9(Rz3e|bZYS|HC zR`h;h0iL%Wmh$wr^z!lxf2&K%_lhM0pfa+_Ups3fGM2b1od#g7_g6)gF*qT zJw$TK*5`5i%P3B)0NwtF-)}_#QGy2W_W{L8RQPAVf3)lm%e)!TdUfDY)^hs!Dy*on zC^H!|5v55{CII@Hyr%fsRz}O$ax>z!GIjRRXeevKH*vpzqO9S3q`OCi{l#uU{It(K zs#pxKfq>0(;*sR@@l;?o@vRr~{5trnDSjV6?XSyG5$3!#vG+!!P+6#j_k&ypC1^NHxX z2}~RvdP>m1gjzlbTEH=Iv;_J}CGHx>?Os!(BoW%}sD1q>$Qo$OK#+CRH{vLzSN)bu z2-*NSyzF!p28!L16mrFK9k?+O&;R^0P^^9H_2315G+|ZMYmpj>R{ysE0RLCz2%p?@ zv>!jH99RCMa!jf9z}AJU9O0k-eQPs)Z?yS3y*%C~6ec7tQC2h!zndjikDb=CEV_8E zK#{-~5k~aXbjPPJ8AuETE3m787;B}ZfnT@}G1hq;ssD+whU18rM?nEwvG0Pl%8f<8 z02LP8c<~naLYk%fmOX!t_-skEEc){Rs>=}7SGth;q5Ul|i2JU4XG7&;)tUjWTlO*s2`!ipq8Ly;4|Jn1da%xWoYu1x8oYf$yzOtIeF z&rcm6Ws%*QZ%#-7Qq8oW3lKTl(XK|VzR0KORb>y;asoey9Hkyaj=?skNjt^YgR;*Oaec3qsi4uENHLQHzY&`kC2F?$6=8Xutkr&L}o9G#$;!lmNVf zSVAN$CUvh>s2=xJ$iy>}wEPRSw@7b!BVeWCG2-!NE9DspmivcL_{95)?T?`GrT5jlr9aQW z1xh&*1zno5M4E5Sj{(S2Gm0a}Fv_qdmaa;nsvhTk>@eGQZxw_E|9z}*{D(TMJ;5tc zQe(4&4R=}t8?@?UTdjWf923pOb#e`*!q~{<8HOj&L#t10tC{ki!(t4{GD?gSkJ7&} zLPY_MBk&-|mXstghB4i+Qq0X>eLx2xfp|oTFFn3W#Uy#R%xww04rI8y?{NQi=NX8v&UW(rn z1^lWH{NR=nh%=Hd=AQNRw^TxBmzeO272=^MnNa+4E)1E>{jLU$6u{qj+3yGeL%<%@ z>nF~3XWG~EX9xKq+!{(bFw4j9sUHycu!#20CRP=(tzL3CjI1!8=wtPcsZXsgcr+3+ zEA19CX>qYO3r*+CV3ueQCgZCoJ&mlQDNyboM(@k&Ubh_opYDd)FtVYjF%L7291N_u){Qli=Ro%Wp`UL$O}h;b-o>M&^nJ zP){$YvnTqnoOe{7k8Y>?W#z2H(r4<|<7;d=Z##csdn{33ahI9tOGkPCRzD|dvD;}0$`A6U zZbC=j6u!;sq$?UyA6R`x-ZAbom!9aQmPUg~s>FSFRYx2^icv~MG&p^*e_S6BbhMcd z*c&HzL-tIY_l)b9(K?A!W`82b;xb(OQ&FJEl~2vjbQI`#@LCrAF|YsN?dCe~Xlvzv zVqkjThAn?|Uh!Q;S?zaw^)C<)_Ca&Q!Iw932k)#1$TSUIWB$W8p1}D=T_eK1j7)c8 zAMbz_h8Aeq0md)SV^Y0lkM;b4{=GT8RcBtW5uqnG(8r`!RuL}@TY_=7#``|{_1z_9 z@0)TPSx((Gpbf!NnbLXJ^{XqLz-=MT6M7?8FI!ZIp>IZeAM}m6^FWpW>+}yuFClL~ zcPZ}w$J$#sRM9OCyPFQ_lJ4%75|HjrMLGloDM31Xm} z_kQQzKY(Ev)>?aJ&$FHf+grI=*Pf>_es%rV*mv7QC!k(EvK;1$!oSq}^UcO|y?*>} zo1+0CF*_>t%Or6`Qj?==mzkyQ*-T}($c^2q>_fXJg-h=rgT$pXhfV!b+PVTS^>zhJlUxO3^_9VE98j!?f)5a{Mu3d>eJW!M>ur+uO!oEuTsDC&s)EH z2SK6!>oe~BdTdudpZ3ZlhnpA|QdTy}H!$V+?QhCa7oZ#~lh)3!Th;*15&NE=@p~HY zjM+Kt&F0lo(8c1ci(Z@6sZ+BRPMD8dAwhC=AyBX$JFa+hIr;)><8otIliAVm6FFr)&8_CFwW~F+N9itz3BV!AI{%x zn(+-aKJ5xVcLdFks+Q% z+?Ng`soKdju_=q{wNLEF*zI9Z$ikT$rMxuPOdStnHGh4$=Pvk;24or*C564V)%O{b zrN^~v$g^rrkQdMIVHvPsq_32R(l7*ohQFe5?cFaPlAGxOl;e=#k4pCS z?3nKUTumiH=%hA!%#OZ$+$WK84PLiYC_Y0b2S>D*AgN|~nBN~>A!p?fKryUIG@A%= zAm-K%)_bVczk}pu2JyayI5>_V>}&b*uvYWh?G>?Lzcwxvoe_(XEInB0NF{G<2*SMh zOXvvcVp-19?*=!(_a%qWD*L!~Rf<1!oL9v(wxmw}2ZTl7z)8Xd@EKi`ACocc`Lo zI5T@nzWw7-BbB|7ZcNjQ9bB;1kVp!B(FZk5)6U)ikdAo~Ng1Tw?5G?jCW%4l>v8sZ z$Et#oS?q|#sze>}>fa9Zv)C|U^Qood3(L}_Y%sJ=v7Pw{+d7fciK;mUCOcATEhsWj zso6Th2`O0QO;g0olP3oagi1rH+5R)-==86YBPZW~q#UjAhpxP2;qYK>$FakeAr$E4 z5AoS_0i$EwUq;6toOGs1cT7OTx^6jDi@gSb7@2|cs+I|{>?yr=v^q0WEIsmMN3=Jw z%$JY8hJ*cmknzy*oVTttjONWj_S!1*BJ&1+Q;y7KWWGsI=XOBFy4x41Sicjj5^2tD zZ{n^t{xAj|s{@Ml)gKgyHJ+l{F(!biXunQB-ML(dXt~$9?|dd5>);1g3YpV+gxM!o z_#vcIx=%N#{Gj4RC<}?M)YjHV1&_>6zDS1=<|%t>gy+}>QZ_moW2j3+;~S}{7CC|W zJUJ3dA<=)3j&B>wl!_Q5E2LFyxJz76NpD8MG3)Y5DZ||gW>~JTgtz?af5oiTjiM+~ zD3N4fftYn2UkIgULtabDYCHmABZv^eHrkm`y6D_$;yxH3ASd@Kq%?A|ec8qy(`H>03KH#s8gmOwSGhcdTn5fj8Y)bR?_;XpXs8YMNirmH%DV zQI%M-V13J022eVl0!l|w=&EO>qx{^fj@OvN?Cy~|{{KDfsQ+)lj`MNx&k=LLm)=96-Xf!RCEzjhcH^xM8BzuJu zqyjTYK?G~-d?KwLl^`K(?zgc=SequPH__=sH2lHX22Wt-{jR*I*})<`BfE?ga&5E8 zMbQ;7OL@MqSCH!s#o%rT>qtyp4z!zTT$o@7bqQ;PSYa3)NqH4|k-vW{(1FNV)}E43oF(KO-nD&}As{{s4|{ zLMIVhUdmz^w^FPu>2hC*5Vmx68IN=Fs{_KNk;k?tuQ{=LVQ5aCZd^}eb`em zp%>alM@|D;)(b1ReFjg#U-1wg9Qmch3#8(rxf1b;&3bZQDpPjj>Lf-9Q&ggFQ*nKN zy_`eIg+UaQ6t#!JJ6j{ibl6`J^+6qz8@3L}S;GK+M@s=~r??{r^fgRww~&3?f+<$IFiP z+y{B}H%OEnD7I<(Xt{PD09xx>)UQOEpS>jr(t#P|AVO-q!Tl~&atZZovxeml&pdf` zA}d2=cBUBDL6Z5Kx)pLWqC(mz336}jz{ob&6qCnbw^#7HQg{P@(T#Qs{{!8q#{Nt< zme75pZm@@x%^Qo>BYlP&)ujQrvGx#v8>J**TYmX{8{^#4s=z<3f}p0k>V}BP#N~A^ zm+8!1rA;pRC=+NSCVFdtVHhbX_(I)9Bv$S4uOWV|_DA z-IYR(wgrhs+?zb?aruRKd`U$Z*zjnn z0<#XyTT(0-If>89S$+R(c!Yg6JjPY##^Mc4X6sBSB-X^O9yno8Fv6rP(S8;m`A zh2@fHo&iFQ?{96Qjl}$t<@sKj&POE^8OlgdIU4I|{^fX7kJ$zskIR4uae`*398DOH zbZF0bxX@H)V-Vc3w(vx6`yar@l1!ghLr?OEpOS;wBzU-+Wiej8G*j<^$XtuN^&p+p zW%uR_n5R;GB%XR44V#GMH?X*@_d>1IWf-vH;n5Z3>W$fnG_f=O$pX zQI3RZhV+*GT`0}mm+G@zdVk-^w1L-KsC)GbWDL=-U-s7sL2!TP|0vf9$s2(6+ivpL z54{peWvT3SJCRxChlO%EZG4S z>e11QOy7ZP))wEOguVkc>w^B+8{0${t`|Vfdg}!1 zpLXk$U(M;B|IXr5S%Q;qbcJ8&S`*BjE68qMgoyTJ@v!0}4R zdN>xh@mA8{{|(dF|HxHkR+3=PQ!~+f5w)ZE`e#ihFOGNj~!+R+vH5|1e>TzAY5~{l* zVgKX171JC=N(*zef=Mk#4OV{g;S3V&>mA}?x4$?MkyJntv@9ZDfO#N~$ z3+vlx1#-)Q^i+P4msqY6Tul#oLi2DwOD{?(B@h>mM+=<#JZqocT}vhjcm76wjCdU~ z!5mMuary)c%KjXR54Y;S|4>35m;G{rI(7tHmOtQzg%hEv+E&_V^hz{Hdh&_cV7DocO(E;emnT5YRuJr7tgE4 z&fM2)V?|o7o~jNVPgT&O$xmMTGk9HEnwXqMIPlvN% z#FA^`7yHwj_oEwMV!{da4|8@@jD~A*1dzD_12EiKGHhxW2~+h~!s;yKk+A)k>C8o@ z@{^5lA@>l8kPk*4G$c}8;yePuBd>3F5A6x=b?%nNs4mg?hC3ABZ}e#YVa8lG z!{wOO?z{7iYnMv+XLrt)qpgk)WU<9mne53VF%4D**;B zTp1r+cK?Wd)cnJ&iL14*p?y;1xH!I+s$DD*QoT+ehCm=OLxq=prp=RHl%(?T5*S`l zydC|Y7LTKv!}b-!JYMQU>vzAF@QOkU4b@vHSmG8%2gj?onye(85~IQK>fNesS}yXA z8@}gwwLN)s&Qh0aB8UT3YrXC`GU(lU8 zx3&k`)e2Hs%c33*Y!=V$>cW-=GLuDaYpFt@U5x~^tF`}bS5JJ40ov6!aWq~)LwWnx zJo=SoSljEHZ}oKuU{xU6C-`09RcmM`f_MvQPUwQfC@oB^>WeqQfeVqmMSt^S=Covk6_=+Hz58_nM8fsLvh_TZ%R zM*`kFHK1Az3$9j^{Y$kPV74N&PtAeaGpqi@5wP6~2 z-`dQR6Nw8HtKWD2!TRriSOQ@CwTM!z2sl?Kif&<^t`q_9p zE0>Su+Abz`GQ_2Y`KVW{QbDUqIDOzB0mKxD$NB>aFDN0A=>KRgPVXh}@^2?Cj@55MCE3()1Hfrp^-z?ysXGH^7m+^e zw3)uBoTsOSMR`&C7t4yL8{5anM8Xu)xZV;tJ`!?2VDSrmBpC0M?Ko9fGjRU^ zhVm3`qs-QPJFU~xr~)B)k17b*k#+N(jeylTbQvNQo-I;)lJ~Weq0c) zbsCVB=(bL*)mo4mQ~RTd1oeKee22Wz8|4N4Rkg5Iub$4hfN%_+`%E9Q)(1~gV-t!s zu$ENqw4*5ZLk&HysFxUkX6yVy($i&%>AlQX6$D{GWN`rCIWC4z#F}BhvR>&wE7d5m zp~n^H&z0(Epi*u4SEU-Pyo|UPY|?tFp60u6u7v}88Poq~FQc6^dTZ_6VdN*FXyu+{wk|LtFD}hdV$KHVPD~DQ|!6sp6$VP8scWVM^z&`muxG@Ak&fLmKBi7pjqf zLbYEHNb%tOB?A;cmp}(0@FhFgVAxBOpQ2OlF24Vzq6yIr!_4br(+Q0lN3U}hQaV79 z-$3us=utz{;+k_PkwepBi3Xpj(Fk6|Y(g1VLIUnfFjXvk)8g7{-{R^~HFYxq5S|>u z@rZp>`AEbDIw1&3xTZSf$AG+&0xs6g{5TdwMG+hA)!E*`u@nTSG;F853xODQ^VSzk zM4my#`d?pf$OYSfZ_9!B|H?3a4xK9!bM52jNA;qy<@+K>b_z)H?xYuQ_@hyJxPyNW z00ck8x7ZMxO#4sirM1SVALwN8RnQ>mVs{nXrivzB<^J%A1A)7`z~t7I(^x#2K5au4 zx<-$bNlg;i?qF@Y6{BY}J2gJRvfvV4f|CFD%`3D=p5b$SE7fl{-<(~jko5>H|$ zCQ~s$IzI+b?!ug0z03fg7Dli&@8TOt#>&T69xUaLe4f9+VA#Wn=IXmDi{5T=dPrqP zTe6tJT=UIT6{5U~CN`5_x60$g$f!XLbSC~X5P4w4+`=xl^DKc+7*Tlep7dLrql5eM zg1GA*iijHd=lj(bED}QdVAL5YQXQ#ID*%XAdK`fC(MVlWvHR-jOGvwG~=>tdteqfGLF$CA{IX%Lp(@jSve(Y`77H zf7UQ&elUHE1ksjFyuO*$MwS-*Gm;mUKIVGT#*CB?wbMlHtLv1Wz|F9?)gR<1+PDFX6ZjOj0>;2xP$p1YPe?U%|J*sSB`NhQ zh%>A2-VT5xAY&n!qi#VkMjf_nPon@R=jHPfPN{*4m;e&Xd=L>pFjXdEG;N2{NdWMA zYYQ-n4%=7NJ5jeSyZ>Bi0?|hG4p8r}c1Xb+2aAChJwPt2i-V=Y9wy158ioq$VS8`- z`Sctlz9mZ!1pw2-5sGyloC%sU9)xvuN36f4b75nRYZq{Fbj){&+KR0f0a3RQv+TFi zL`k#Pey+YrX4s62Iq(fG0Ao`)Os&@dM?nfMsDsC8gk7U;8LX40o+chJ;tK^HbrJzz zowT@N*cUQY@b(vJKXuv_9E3tGeBw8Ma@6T%n?R0w@D(8>U?CljP)ojATKcpyb1As; z^D{8k-3522agDXe-rNf^Xo3&0{o>s>Df;yT`!}%VEXj&)C6|f)>vyBu%OWABo;yf z+c5HCAjN$whxa>F`bC(56|?_@Qjde2PJr#Kqw8Dv$hBEJX5U)6KsqaYJd6N%wG_Dd zyl`C6_R1-RfeGachFQhO!6w>Nio_Ss_g7fbl99!;@kKcHG{J+E(6*0CPe%mFp9 zIB~uw!q?D!TBI1nPq$v(r8f;U%@ZZ&IB&IsU6p}=Q9~5iKRUU%Hc!`9`O1^yfgTwWKtI?QaY=ju z=trv&)qTx%Y3p~uq@M^q;~XYjF>5hMF;G3X^aQ2EaE|goe`8Y1?*pv|(q8}=`z&dO zr~9H2i6%dZxB9xn|E-u&NQ1X|S1@vd%jMAq$)!4KUY+%Ha3G>w;!lWrtZs(}VD(Zp z=z%F~n2DXognb1nwIR>!UR9$>&(Y0OWI$>Wetv>Ucsw)O->@ilqMBWS%Z!tv&^QG` z)Wf?%c;FB{8^cz=6Tv_($wHLh% zJZ&Vf2So90kJ^(ws=AiPGC?L{=HR`OAIZ~)gQfIO4|BJZ&}u=G2(he1w9HuSJv|vD z?Q=ZVYK5##+$d@W4Zd23(CVa?fOl|=cZ?}r7Db)5e+se)47V4ZfA^-*8O!ME z>5H5B*H9Ws5XCJ?YNv=anrg3{VPjPa$PG4Y;fE;iam$0hY@@&Dh>)U;8t*^v-1^kUMJBP;vk5_hwY z86C6zs|3<449kCmUmSmO7#9-Qe3ud7)G>f*DJ(NMUK40&Vc{SsJ^`no(t3ie;2611Y}j^&-5ShE#`pWm~Il^in_-4HS@%v`L=0^Tu7-^}L9sBGUG{cFaATMvrBDn9&uL-k}^Vr)g~3Che-;klb%v~X?v~Nd$}ClBdr%D@RreK)>ZCDnRERpHTCx*H`QIqU ziGNUxswxx~$HwA)DhDQ{v1G%_l^i>NVT}BDMpARdwZDumnyglWnYbDg_hz0(=Vms3 zkh1cXf+daE(XEdGqto*}9ahCjVY?$n&JV}zgkVRbediQ`ZaWg#(8&MC(3qcyO(^{( zpNJXvihG|+v6U3RsD?5*@n+J5?g(f-iQCY~ub}_f-DD}T-su;Li&>~9>0U71OSRP+ zq-J97?S;KtlZ^Ov8>I7|>9w(Ef0$D_E>l)Ny4|B;`*6?4WwJIybradShvu|8yJ^oZ zlk7wtH$m7!6&3hhf%vp){>o|pWqR;y2YxkZi9?r>wN1pC7VN~V#oCq69xa@%ua0;R z90(-g!rra;Xm@rs(matC2B2&B{Qe_pJjDA;(%AgJNE-d1MAPz4s}JPQ$`V?ZeOUY! zq*6w&-6gsm(n)`(VXaZzCmcVuUN`S9Fa*Tcf35sH)Kt6D^xf)}-XpiHpBwDc*m4qA zZKb{I)1zB(egR*Cv^nu|vm7NQ83kT{p=vA}Y--{hb!5@m(#j=h7XR&%grn@`{N1** zW3frCl5Cr9m*?xwW#?tD))?l6*pf2qNvcC%1kRJ$0o8IW#(PK z6}yzay6&1Y`xSWAee#O^Y3AbV5+;LWjc09P?`%=b5x!&T;c%m1OnskO+p$9EPVY4T zQ{BLf<3b5OG%o|e+p5CbmY1lQ!&7z6g9*drQXT&!2g%yufgPx=u^9D!p^s z@17j`Ff0nyEyo&6%bo5ucxkdz@ug7bU5;LkZtR#*5Zi4By2@j&Aj6RVO`sO(x3G^0 zLKnOdc`BMy-TlV$Rxpi?pWjJu5r4a(wu30<*mO_+l&RaEcuE6fC~gV>kQv3N!P-J6 zt9x@P0$bn%|e6+`9(`S5!l9<44y z`i12WrX-(LT4H~z`gdjf5BXiIpeKl<&;a#KA;-)ONz~oJ+=|z|l zsIM3b#vgAs8~EK`S!eW%Cj$6m^&1RyK=x?t%pixPpUsQZo+`^ICTwFBp7X9>*yUYZ z42%A9NsBy2XCOL#28~)-ubqr9^H*6;%ucr!zwtV=1fXYK^4(psvwk9N7JYd zxae7wG|o_kiwGNaL3dfPVV|ZN#sTUve^w4%A#;5beR)XLrle^0Hy?#VKRrW_#sKt~ z)N4Uj_KAK^luBcq*q*bX+ZSU&TNx!zOssS8$0&kYBcvL_&o~omIc#Lw+Foq2Zx*oK zSxUl$jtxHXe$aBHp)f2?1;Yk}^tW#3=vvk=#l*zEg?TEaVm2m^mUPI2NM&QSYjO+t z8ly>p3zXQMa&q5BKB9*vKw>rz#wV}^z&79~nj@a8x6An*Fs^?|9B6oPl7MM|wMvVc<5&h}|bv${T6fvvh$zYbqzhK8k%DOtG3fUPkFaB9CYuavIy7OW{lT zY;_EQjlme0C;t6`5pN#g@H7DqudFJR%2_Vnc#Ef6g7XKoEUZhxS|xgLQinr(bbGXf zr)6h!T}neN6%|mX3DONrNUn_^SZW3 zI@%>2kT@0`VL(t(y{T!ELvx~vzDrxM2!B>PhWSUQGMEarSFKY*rBGLFA_9S1Y1j*z z4Ls@q!z%b}Dw-?KH?cf8Dl#%K9i|5I`ZD@5Ic;19G66lsg!FU?g=+8rDp&hlQkn%% zq9z*mE8FuEtKmR10qN?Ytij*k5wsd10l!g<3AGGi7%c*7|4u17sxcvlG}}*Toh48V zDCVqNjywRBM=ZKCiwY}WFkESBe`7K%BHsm{$q7ChYZp&V=Ua=c5i}TzuEI9+_H$@I&u~^!b(!hr?qjd ziw7xR2oP+y^Rlo`zl_mgWVSm{5_qeiV|nT(!5Yqgy{biN?%{?#}D$ z^}4naf9(ndII$AlNM&neu^X1j(jeO)G4@>`c$UD0l1KGR`Z!A|M!kH=Fdh(||PC+>peOV0R`c*4; zROsm@B8;tR`hn z22CwR1XXv;I8_2(1P#!u#^HGzOvV958n?%y1JGD^jRwAKVc<gTC#C=P%I<95@Ihx zY(vuG1hgWo8f~#=^pun#hdLDS@i7h@&t|-?Aa0ye(T@ryp3AOfKaxuFz1WKs1Xru=e#PIlE(Xu01t_V|2KYO7cP`+c`3F7tMzaO956DIMA4)uU}na1He1p-UK#fxjAKrbd=s6R+LB$QpC&$6&C2 zl5z6f0CPw7LNK2i?*<)$^e7>o(om(m|0{XYL2ObP_>^-znvK7~M*OvYVWx(`jM;z2 zs~>=P_57OCR#S6i%80>(!Czl@D+|Iye;6h@C{7R<9p!$gNk(*kG>w}D0QnQZ*>SH9ZePrka^ zhLja^_7PtXhfKedQyD7>u`WVz`dcizg>&_3oAj2vDqAmnpQ)4f`u1K)5hsYVMP&v~ z70I?bCDu(Q?O=(|9L$?mYPox2rQLj6N`u-$}6 z8kM4Hi^sU|V0*HhFWq6=8lx(#ZU{Y#hJ3CXWn2+otbt^i19YZZculH);itZm?g>G& z?yTaP-0PuJWK3E+7+Q>M7N|>UbgtWI8&(#1U~t~D3+GIU2Iex?x%&#Y+ASGi5U#|$ zDyEi02{Fxwhyg>5DqG5(EWF7K_bMCWF5kiFY7zZ-EDyY8eh8*B$suN6J)Dq}0^Cow z>D`7>{Yy!Us!y5J-yYOuw8E+(5W7JHGeiVKnj7=_M)D)}0U#reXb}wc8O+Y`(-jZ% z!TAcQmv3Fri~S)fhn%EAz=MHUV3+{2M`Ih7-&qzsKD?^Jk-3;S?Py(KoX9MeES!f9 zz%*9$rvgkPYb`v$G+r<;Bi0C00ZgN6@?T6N3>t6S`LBj7P3{oZ|!2ODW zNoX#&^tWsYZWR6Xd4qMs;ZWc4AAIG~!E*kcL~La1CoNj4b}EHpZj*^@?_YoIrD8rZ zk#LpijI$vels=IQ4;=81{R?S)?7j|=Ws?t0*;#30j>H{!qJUu|ujB!%8s```cB{j$ z*Q1)PuCjK#yVv3r$|sJI;Cc?^zS5vf_@k0cdK{P(H7C!aQVk~+PzU*imqg!!7IOGWnf%S?5xko}?XoFfgSB8K6F z8K+%B_xk6M_45x$pUt|6=QewIk85w2COQTrc0mDm@8x5Lvvw30nyf#&mOkj(th1Sg z%5=lWcc$M)V{6SbOyDChv!Q=X7y5O)k9lR3R%3k$yVLa^ARA+T8howE3)Td<#wdVm zY?$D?(jkcGbG3DeMpu_XBnztkW~7$00H>LgMmpP?EApaz%>&wwm z{(RTAvCHz_{IZ)J!p9ArDuT4L4TY0JO*f{nv8j;h)h#?6$&9O>>W5D+0aFL}iuG&z zM;E#eORtJ#6zv(0e`eSnH*np;x@G^PJKaE0RXTFKcYZ3pKeQD>apQU=?W%a(_Grlx z8<4YJH?g0_`&Lf3e9hXxr^*O@?7ViK^!2(x;fV@3JWc$_Ix1hrZm9^*iDPxOT!w4K zPqSj4(Rxrb7W~|v_G|}nnUUnY=sKqyJ91y91GlFMdNS_Yh?;*c(ej1==k~N^=(kIs zyZ_LhuG2IGx2HclPwvh%n-D7=U2oJ|v;=Z*JDqxS>iBl$%q{}*A?Cg#ch1|61QMYZ z_B%#q9D?LD_%7RyU%(BhJk$PKz>M<%(WFdt;&A!CUx6KUXnxsVBdawL4}nCO$j+0S zSsU+Nx}TwoN}td)zx~^Z{AK>o)K9uM%N5FodL+ol2M(WV(DoQV>x?2fnGF^nD)KkG z>NyE|R8H%%Eg!A@I0xXyg}$`QjMl+2UyE|eOj&a7_B)P?pZa#$Pc=_O?tCw=C7>c1 zK~e%K3PhBZrxSUORMtAln9}s2Rf4fdFES0yoh@}fu$w1yXF~6hjA9H-2FOou4c!sTcv!nH&E5XS2g8lF zI^$1FE03g`Q!^c(Uo=1?Q5~`QWKjl^8C^HH*)0i5>dhIy}jqM-GS?RKIKHZNAXu3B>d_O-4&9V!$Q3bd?QF?^yYMt)VQ)Q|i^QJo?Q>}GpEO}HgN*`Q;A8wed}EG>2#uj8oj_`Uu3Rhv5$x%7mU z(GZ`4m29p^q@z(dfdH0|+N?~V?b^Wmg&I1T1W#Fw`lJGX1e48}wOO^dsLr-L@$p|F z;d_b#B3nm%HEWW*P=R-5E=9AErzh8kLkZ<9%$^rQ9v~^{Kh8$EznqPH&(2190u@8H zU^#DP=;9$-pLdQg^|36Q94@`7i&`$B!HUFFT)&!ml8e5st7EvA0qwNr?1(4+k>SkL zBebbX-3;u54OZ)+DsT8}F>(X3NW~Eii1Q(&qGYrrAic2oRrp+GI$CrxAX#BpO`H>} z?A=@J$BFLU?;emhk!gbI8W2`GhIGlZsne-l<_v(Nx1ct|#|^Pqcn_~7sW-Y;I05?Ta8M4LFP5Q%NBGv|=GS$?0UEQJ{AQ&_h43va0d8on1G%XBF(3AcJ_ zTe9^TfvX*Sp;n2vb^R-_;CN=e$Ls+Umr7oOZQ`}0=CLDnQWZ9gEb!2si=Vs5rI4bM zsO-^*rRqaZ7q*}(@klUW={p*JKsQ+u4_~s*v+I0W(TPPn09gwBnL7Tc&K@U6TNiCR zBzkZB6|-xiKRp{8Ex^Xc8flom=l-%;WL9Khqsh<$oWnmo1x{ai5_z#JZk|6d@8u5EsG)n z>q7~bs|#xknQ_Yf7CLVsxbEka$2etDWG(&#s2g3#$5lFK2zP;4>; zlqh|0ju(dDkNyMIp1df}PXms3OADnNIWzBsDRO?RWujXGenSRfiU!m7>11KT-2(tL zB;9sko|~P`2xbqTe{A&f@bU0*C&kQI78DjL!tW(f!Uh>u0v(o^iOh-d70fs~a;mUB zGmgfWg@zOT)g39GDLbC9=dW*GaI)V+Omv}eLUOL->iBc2hRF0lRBBVQN&s)K;=}u?1 z47ck-4>W|q2XMP>i=CE(G&h&Kv!SD1s?ABtKg!0d&U5L1C>v|$a^u0uM*ROq**N!4 zWg{EP&9uwe`oc}ekwRPR3qnW@bO0-)SLvf4-}}rQppd*RPlh|-^LsB*#NTOFl2Jd9 z>;0T%s23eHnz0K$foLR@^Z58Xv-)4x;sj`v@~VNF#|~eZP<$Ex>1>?;<80LBvO>h8 z@8j?p5l9p+)$fhMz5yW_GQr2WSI`&Ymm>C}k)rPx8piZ{D0(it+_WmLwB909LW%(O zXdgC8Xp*iTrR?wX4xxdOT69dM-4htJfa7zw7BZG6lhT!%Y$_6(k)oZVS`j-%Qt&h+ z!!u(3DcnQns|lcN6aXt5k%zI1rk0NsE_NYI@ehT z3P~$dO~w-_laX|l3o&W5 zv#O(6c%cwAKQqw|A}T@%+om4P6~~~dRn`#$-ZSuu1+lg5;&lPxGUH-j3{t{T(q-|7 z`Nqhbn43zZ(?Td_TRxbQV?OUCq5-euaAD|*`7#1h7@#zaj8x;3=w1nTw7A7V0fA&j zW%Wk~*S49~FIT(2kNH8doISGa|70EWkZBnJ)^Yz|SjTEG>uC5l>-bYt$>YILrma!M zlcnBU5#u>RZKc)IGx1l18Uu(>=Z(;OevVKlKS!vG{xw2v`p*dU_U}!ztNWET{?EhB zDyHXW zPT^_BG43Xp~G`ou3`gWQ@Tr#f&YzAdTbv zV{1%TQyJZ)``gwi|7>e?{%>rJkN>bWa!b&ADtJ^bO^^t=-eqJlDrPg2yv5;^RYgf= z`UPd6K!4+QG@mC=-w@6yL!6r}hZliX$O?OV1rW58%BL(pMBnJ*NCDhYuat?snT^BL z({y=0|QekW5m*N=K@-QG4XTfKix;&ub`CAwh#V^Tsu-oaLfngu<*vokd? z_OO~{wlO)5xS(kX2ZdCIm|@LmDq?)M%DvA)|#)QenR`wa+7)57JF-n!=uyw(gW7#Vqy-yT*w zCBUugYZ5_aQsUF3lzxSyAd95KIAL{@R0YMFXe&vLa1xKmaS4L3`W%v`2F{?c)R_yB z01s=GfZ)w-RL#ocqDv#g6$`N3aAg`7FZEWe%E%sHzqgv8#(Mi`tIZJCC%~6Nw@5K8 zP^`a3EZ#oF0dPhp+;^R?aUq_A>9e?n_xsjuPu_fAUmkxVQ{(>mN7p$1tZR&>+bXk~ z&+Q~1tfvy8kN201R7*5r0s)y{hPzCx&_Uz?VZcu+E5~OoJzD>%`Ke6rBV-|XT!3wj zJu9i#?iyuv<}rv&FY5-Q)N1f7F(|S7kAd08K%K!~D9mOI!nVgJfWp4tEvh~XL2-Qd zl5{=3;EZNv6|0mgE3V45mubR3mhW?~*t(D;HTayNrUz%J<$(+}k(uH=kf9Fy*9^7kKQq+! zw^uJeEr*c&`qlIJDlkPJ=vm8Q1}g(?_%?x4chdir>@gy%jKO(~KE{$kBcxI&<9-$!i8EKsNGIB1oZ6 zA~_pIszmkfIY=VjePt(X#$@?S1Pi=F&o*ob=>0+u*?EYAC*jM>d z{KF{|P)Am7H2y?!3gwzstA~hAhtAy&p(c&ox#9nj$;|`6IxZKKv1<#85KF#cj?(R-0328y3yHUEkOG>)FwMBi-IrsVQ``r7! z|H{vWd+xcyHOCx7qO+Zk23lQrR>2ne8$2b11OjL)(5~TqO?&H8s2b4MRP$b|@#Vta z+MaQ_vy+bsTErV_phQhw_<-@VM6Cev4fLsi67?3SM4kGV67}t$619G*-IyCGhc-dj zRMs{%Ffv-%gMhH{8R2Tes`(*a;ZI>B*FS}gUVns*c)1BvL4?gY3@y#elkAY{;$z71 z*7PV73gYx?F*y&aP>Z+EBTae z6v^;z@z%M}^8j)L)u$goWAb72kkIowWc%ya7A@Zb&-eV& zkjc_$h@x%Nw1UjdxG-W^qu$4HCI+Qw@&sN_ZpRbp*6Bse8FQ6vEnltA2?A6fi7 z-xJpNn;J-t35>NWp=|lvw`O}iJK|4G$VXh_E#hLIKDMZlffjW-JE%p?478}t{@S9x z|I?!Geq(5FzIW|*8m?K9bkj-`@+5DaPCAULpg?r1<8cbJ>e;rl{-Z@b^sg2*o_+%1 zTb^f0Ui8`m0#{d%z!X-Y4$F|{a!?^U^}EyEHfpH3f|^>1)AQn?S@bJeEBLoH>V2R_ zee$zLt%e)CZ~eeKbLVTtwc!OOJPL)n|A{M{o&wnTO6_l(j}P$WH9mP7e)fs4Bf|m~ z7?_fiDUC_feN)Ko(N-~@_)v{yxL`?~_jP~fRYRnLrZRMmAHA!cxvTtib$WAk{6bD5 z^-B@z`?>r<*$2SP=4P{uktN|6j;s#ERHq62{H? z%;_oN*RQfNKXx{+F3Q7t(CQ87#62~!PABR{jMlyd@ZhQX#r?E4PCAy%nIBxtgDJ3n zt^=%%AB4koOg9HPLDojPU)Dwp?P;e+Z===RFK^@djLuJQqaG*VZJa*%frO-fm)Ot} zxB8oT{PCXa`6Rzp(4p$FOug5Y?&0~}bsxYyni`3UHavL8Js-FacODCZUKKhq+tb56 zlju>`6#>MJOjPIfO-?18-=3d7OWi7D;OUCeJ|lcsld#)&>1n^TJPf&*>OMtFw~HXN z~yx@uryZgUV& zCJIS#qUzWRt}+3y~>{64rx~{1WzI++)WZ4xl2LB#z%uIQ@adDm3=%ko(ywNm$5D<8iM8$?g`js#_K_)sh@(bd-GpDH~5VFZTgu z;{l*-+*)G4nzc$()+aJnekBRCsZUo|2+H0!tSn};^CAIlYHQ67Ji%b)AJ(~Kfw>Oe zlo7uKa8O4l@|>`xT3J!qF+yMl`g;T@xkz? zW7RW|u#v`5N!kJsHmabcy{!vS`YxMlP4cXQ^Hp=<)sx+gFluK7A_apu%emJM6JY^sTQp1;KZ(oFo-Wxg=rsq7DER9JLoPPJ*toh8Mlx#! zxBeMoeOeg7U2mOpvul8|XJMtm_C}b#omo5u)fC?c0=BwC{jbEL-6OH+3lNL4@fUKg zX=MJWxx8+fa`m_a)Tmz&=trbrPmfh3=E_tglBJkv12|z*MjPy!lGUIYnb1ZP^Dh(% z^dfWIfig8+B=NX`@JtIz1bf2hyyvXlYWSJy^H~vac2zL;DGuLlDDuzJ|704l}Fc?dbkjNXEJV5nofcN^l z3^0ckgmx#XuffwOvgxl!H=SZ@EFU4fc=|M15c{dtckPrl6ozZ507j;QR0%^9MdQ9? z+KNOppTTd}Q2h>y6e5w<$tVVTG?vT}Is+KV(}s18{#-RZ%0W_QC6Q3Xio}hr!IIqp zYv74`{Rq}M-YHl5o7DaqiMj>h#@)hQ^tfq=9@;15?-RuRhuKg))U*oox@)OejoO#T z>H3(bCgNaaAS5HTO5EuBj`6LISCc|jCzB^}oe?mnWUKq9`s-$Y(%U@``kr~#hn&p5!GxXz>&o3J*B zJ0gfqhqIj%*dt0+5H829Bd!A|yV!Ka=cG0IW6_3uVmnd2aKOporoMd1Loq`7Mtf{G zhmI~JE^c`>9PA|SX#!YATf8Av-}@XgT6TCHpJTqWuLnJZu09x9#gD#5 z_`cqd1#C%Vf3n{R^+m|N@?c@W3v7(UT!=?@yIT~t2`f+*7QY;WyeU32V6=K8sF`agi z`&K*O*8a{g#-sdq45R4}b?*8qL3FG>yQ>uLp9~{P1JpkmMx!LDNT0t4st>r#HmNpY zN&Y>>xIqP?7-#=UF+wGyC4(5$KMZ4}f713zoN{ukyeL8UC~SXR2Tkw7X@qO3Ym-e}uFvMynC8%Ibf2&rLomJodYWfXDv zphUTJJFa&5b$*3GvSQ@8apfRhtwFNY#+?1MUrcD`t3iWr{5Vk*^;FG6{hyGgVBv5p zWuKUFt6VIi&@;khaS)LcZCrL0n`U+54&|`dquh{n;jiuDEU@jz#gl4io5aF?r;);Z z;Tg-U)xa;qTJCJK1XZd5Uh0q1zf*|UzA=Gb84Mrp1K)Z&&RXf`VC32w)0y-HWn7?9 zoG7NKzwa3hx|X9kSt^_?0e9k383@lOYrAXThfp&eB26z>KhV2yI zj4fDa)MEK1j@UOId_Ga5uMlV(YxTW;{TVHvfPUokqeYl~qFl<}tT-e!LuY9Q>gs@QFF7eh+Z;J;SylRW{X+k@)g+4-spv;O)VahBu*w5ux z^)l$jnU86GSGyC zAWh?N=6!7gO~jZN(nzy|0a=K80DMS2eFkeQQw2T?6C=H*umTwxxHw~ZrlKE#E1+qt zVPZ{|s}@7Fqr=}79f*mfTcGg4QuW3n-nNa6e}~DSPjSr@h0nwy>ViAv1oaj`9PwdT z(5{c2qQwAHBlB9@i9uKCGkFq_s4)a2YBWm3g=D|H{6jeEjd7EI`X}L-_eePE^n{sq zhLd6}>`;A1xFYBgn8!wijbu|53mRnoBx^ggv({17FS1;6n9ya^2dfR8lBKLBA5vjt zlR(j)8pa_vThQR`!;A_jgp+xMG4=fgu?VILq$BW)1V(HWO+#=)eGm?FywhpD(@~zx ze4)o$`?JW=no9HBTn|wl@HFmb&7hu$nz}wyt`iP{Qppx>KF1n3!K`P8+UZ?ns8azV z)OV?KVLv0($tR6vK!kb(6rr}S$a4@k>{g5>OO@d1Gp2rwP@fw2Ny5hds5EF)2njc% zs^_aOL|y3hMsBLQPbt8ES_{uCP*0!5s)J@gN_nU(os1!q>&)^qL;dDwhWcnesV$F> zr_C*dzU|b7Q5;|zZ8aht{xFS1{oX9RTJ=AfM(y`%(by823Q&)bXd;CN}(Nl z2`-!FH7eEN*CVg_hp;L~#Pk>Cd^(qh;iyssB(z@k8nja2lyCl4G)8pFbXQ5BMPia7 z490pV0B|6j%4({Ucp!m@0dDbU>G)TKw7!P1rtGgp@I(kC(vQa<1&tfO1dZ^&1dYe5 z3w_=HU@$wA5Q+(pWYLSak|v3w^cw(BCR7&{~7qx{~G27d0Hg*BNrDI<(+k=_D!9o-O z(4}T_m61A!zzZ{L(293eu#HlBCK^KyOeVBsc!N~~OdEv%s!VpD* zn6+&0eYjZP6TaI{nVXsaOMF^`C*8j|6a>r3Fb5@+qo%K8%jaOl4M(InFMq`rovR}h+OyPkoNI=lFWc#>FKI}rKf)Y z>FIE}mb8QiX`0`L#sM@ARb%CRkfHGlKZ+ScieLh;Cy>~zppgSTzIwN6%W*JOi0cQZ zA9&Imi6C=6txoby^^iabc9m&apO>oMFNxQgTeEp_{p^Z^83lGHr#L74OVVG*7I(g} z_ot41qGAw|R!^VJx{Q{-0erOQZ}e7Z_@||QrKd@}^W()PzOLs4*RxLM`WTJ~ypQxh z+jzr4Ymp(CNGq6#;%myA@kaQC4e|Si0jAnl)y5%U`az$qapf3;+?fr0OKq-2>{3~X zG1=kyjY8tp7;;!YGuP0e6H5Z(xkNh14at(mw`Z-f605-+7E}o6JcG_mjhntRr_V87 zCWRSDyia~LWWt5E?V|~(Q=v`J-ZMfvNuaX9$JsElfN(~57CFvFH>#NNFPF2gv-RrF zc~c;z>Hh?%m3{}P<=Ytnq7gw6AR1eL5{ir{~{VS{tMA~U>}5{ z&oT-k8Zp}+1Jpl>Mqd!oNdAXtjCmv)^G|cIabcA}M5FX?qEY&hXgvB!G;)H7M!U`+ zsDX`aXc`50p|nwUWXpK(Xj;HNjBo{dLxSNxauDb8t3M4#t4}ro(ll1tfuTxh@5U$Q|a{Yg=J9X(^8^AC1f*_QUx4TA>r;RBbl)_BKSeR3cPWodR4>$ z*{zvyoyh`B&14KcPn3 z5$e=6%qFMnnPq>7I!7xbP3a zo=MZFUY6pK1HY0H8I=9zX*iTvgp`K1ykEM;DCRc*l}BBpOd=vk*NF0d;(ks2?4$ix z8csT`pp{PiS~pb!`}v%u-3HS}HeP~ricWcG0@1pw#pwr^h9kOal@`vz3z&DZ)@w}X znNBi*u9358*-por6ZwT~=wak{Q{zKVM{Avs4Y_m}S{1jYp~V^3Er-L4nfr;$akhs? zU1P4j=R7t4S!j9walP9JZX<8U>h@`~OYOtK>Q-0Z=G;f)VQmKJk2j*qJEoBveAkNK z3F<_byk#*|gh}@i7zv)EISbc*y3)+h;x~O)?_o>kb;xgexN|qS<#Jo`dSu zp9N|{M!}((p6zxLT6Qp>Vjm;a#p!TsMI~gp-u!x_Ydfmapvw-))pIee3iKSy9<;3h}Z20@) zR7R9`=_l3jI(JUiJ6ETHbKAl@WaAw&xjcZ+u}h#C+YRh;xAb1ky6?yFl@XFdEjaM%(+ko1)}%B$`p zThL}ohwDZDm>q0!s19En=>*5Hq`&(b=e;|n+@VO}T6@=r$vb+NG4Fk{-d$eERx$jo zKTQGjr*}d9>C$mfe_H7R=ub-}vAwQG4WWj3DhwgaTyNheJbI9op1E}UDu@s(eXFia z7_IGp05z6U;GC@5(7k|xMhR8!VRKJ|D>q>QCqxniu_nYDb1hlg$PAu$^_T89Pl?E- ze|Z~Ce|j5t9=(m0Vj*2r%+R8!rS!S5Rx_I3@})bYGcc(iPG-?tsamN9^yBScF5b>A z*55f!+(U-PQshRT%wOl>1fYkyh=}6UNvF%xm=vn`V#Gk?i4hO3v4T-|-J9URfhp{Z z?d(%SpW3~HZ^ZyMO6c}iCv62mIyAK`&2mFXiK8ppr~y|hE_nc!Ow%h?C>Yk9>trHF z%Rj=P>T>D|pb{~)z}5Cvuuu#I2JK}iO`$EQjJ?6szmf=?>19ti4y{G% z_CPAtA7}LUk?Pi-{0~UMu@vvu%|%huO*frYxKaxus49P&8?RiuKU{*$jidiGH}VMO zY+%BH%#De!sdlG&N}?bg&%f`#hJhuQ7V6`w40`r3t8AzdY&XSjDN4Q6P{INaMAaE5 z*zoysd6L1}ri*0e!#Xo_=dlA+F~Xrm!9^=GY1ngbxo>`Ku3q=_EKv%9=jQcM8GYH> z-dz|bQ>=ZjBVI(5tmFqQD4Ht5v-C40=}E|u=i9pX_j-}KMtqxz;R{HeK=V*}%E=$O5#mI3RCKkqZ@jls77a zJE1JIW3XBEW}>QcV?wVwfBlU#(fdZXv3J$47_a8*p>c8Uy?1nBfT*=C0{tu8qMR1? zdOx2+BVdsb-s3x~FQ`ymP*_<{HMM3OwAyt%VSKjo^jj~)dkr5^Bf#7^n*MilV=rKC zRDCoz0-tG`)sAciI`*I?2$|C; z9xut!It6DP0UHZk=DVX}uq)SP6yQ5L9ofF{-`}5iKza~jM|>j!;D04z1VA}%sve)) zG!;~#e%fC0mkPDvp9-}F!pr6h?fcxz?Ss0xjuvks(^R2Gx55r{TOtGEKjy}NPtOi|}9MdG6!CSR_Lf@Wchm#o)R~?-Z8c!$Mnic_GK#p*Ca`;BIsW+>NyDq--E} zqY}v7c=E^H*zK~us&}-yb{00=<@yvdimsI(jo7a9_8wQWX)X~qtb;7PMEB_JbBl~df%;}w81E`??uX3mrpRr;!}D-SkG7u zc)+kk^Ev{R#>A&=Dq3D3J8k>LxLig^u4~o}BmFv!A_mb=CA3P8nWaD?}E6r8eWZ;m!H^iKS2b#NIuhsLTSCQ|6ms zS)2yi)kz#9DBch*pmlkdlhum5@-y3;t&8CU_GR%@)G8zKA0rbt%$%6TJ*+{Lv0`nH zL}COb6NEwBzwtM6{P8!kcT+a)bqvXJ?C(_{mTQA&<>3&eS>M7N&nniZZ*_V6Y)l!}?(KJ5V;8OD~%4WGa(0#Ot>qEbHx>ABg28Y9kbWmh6txIe`Vg2L4>C9v zUiIV=Rs4plX8py)>1{G$vFK zLsFwJ57=nKS=iZuG09qhpeZ1cTE9Y!%8@@PN8O6y{-soHC{n@YN&!X;TukkG-Yv=3 zvUTLj5M&IKWy;D>Tq(cV9EJ6YrOnC1qx~K3(Dc!j#McpvotI71$+^(M9@B7IUd}O_W-L{n0 z>=PSp0nbfx9>BB6%mn*X8X|Pt5)whG(E@3zn%lK16`Ckl2X@8JuajL>+S>U-y-SRN z2Zer%^8j~72nBEj5cRB3&wrio-2D82unD^nLP4A*p(pDp%a>%>*uF}&b8|xj)sr!KM~sy<27&bpuL<3+7bZHoF7-~Z}p>f z1E7v?N;Lq;5>uVtB7wBXgk_RUj3#;saJ#BX@jEN{==FlpfG{?iFE{&O8JXwJ8jaxj zGr}1HS3gCLA3-9=6!yIFpCU)`e~KLItAC3e@$};?7rb6;X)QEA*xlhwlSvz5C?c4F zKsXX=m;#+rjG{MpF2I+<-R79iXSxQL%f{?3>TCm18wZe4x2a7wX<&rNk3eEy-&MoX zo$->u^TX{Iz-1*teo)B=xC)Gk2avQOZl38-8<0N(WS^@p3S~Gkn3>5}%PR^!Mz<`> zA!{~YpUYxH4M1dqrh2A>zsJRKm*iR5!guh@JRG{TYI^uBG54K*fY;|$Bj8?fbeI!$ zhLxlu-0nO-Fd%e(|K`dbG6O7wjv-;|s=8$e1Wi~IlqTg|F>!|!9c#2J)BUGI zT?2He6Mzo2*9IpS%DFL-COuf|^~1Av*bAbN?5D`_0wi*j{FlfPeJfu1 zA0o&7z2727JiqvAj&fv<2g`@c#dlA-@Yf&wshuOxVG`6nCBBXc zwMbP8oN$KHWfL=W2+H_?0EE($`uQEO?12qUC7>ZG@!)9@Ih>y3Sa-&ILoWZz<0$^< zaip%>YPOv%6(UL87xz<6R`v!&u%nYMFe;T6XigQjR;5C$7y?gJKkhA868ZW zgpEpxi}T&7h-=rI-Kz$Dodnk3A!_tLA?gkwL`^9}i~9891BOX}sbm+QAZ>km<=nxgr%)rGT0v)@s^QU^TKT)#FMLT$4WRroT`7v(Fbv2p$fS;Af#}3f=;_wdaJmU) zLM|;J*IX~CBuQRxknm&aJBaq&=wJOL7f|R;6RK`^2(BbAB+rP;AsGoD;qm0!`6Gf3w)!rAgOTpl+ zK%-11)rh5=;ZL}xjEc-tqIqe&v90n5^MfktL8a)q}tglb5sz1uE(|(h9e@rz)LH_LWC3$r` z+4G+xqylHaZ_rEQJAok=lPaVDntz&8w`u`?tS>5n88g(^)HHuxee->yB#A=+u0rBH zI5n5#7)fYR(a6y$nIf6SfSW29)Z&3G zd_gEd15+ZQVc{e%p36lFJ}9DCieR1cAzcS1U5qvf;&9px(B>ONvJKqyYF;}Hx_!?O zP>LGwpDF4pAVt0Ln4%uYjlW3yIF}*wc1`mOTzo7=eloPh&%K!nD>~NnQ*VmRtTN;~ zI^pt>-t#u?*1#k!+#ke|HFAXarpi}_&z*yLqw$pAPU`YFv#gonbXXbgo>(@p%jSa3bzImzSHI!o@Ww#= z{t+XcsW|w(a56m+47`+OVE$vN=vhW;UwbuP@ZAFej7Qx8RP##exwp=Z1>@{d2!?_k zE>p(A@#$)E`?74*V)<%@-NQ-8;=#>XoUIV5V(BcQ&8YrN z*0I%fgCcVE5xbleD4r~$<1;yEusCF+EUHnwMG%`n{D)ZTPQ z-gF%!4R?H}$(hEC!BnSVRo8Oe zc&!ei}ZU+j}wQPm6kQobDTyKb*?>;JDxCr722!ZyvLoNN=rj z58aG2q+3O<`ld@impQ2n7Nl1*d0lGLChZK% zI+u!aW&DCy-I`3_wRJrmkfTQaYmT}b$WeExf(gZ@Oi&%pWz+@5(ORY!vA)VTe;B+v z809DYyj9^m$vthZd4o0h{+>=TUX2X86LsfZ@EHl%2Zm|7mhvMtHzbN z)z_@c|L7Y*^MhL~2i3W1QZ(dh70C>{JrVn4+m+Du`U-*IIFs{Ubo1ehC!;%BHT}lI zX<#9)eo&WnWXe4Qpx^Ar&$?^osm#E8dw4rXp*?3=ZHBO3YG%?`uxqcy+t`sOqHI=w2f4Axc2T$yUFC|!*K1z*v@aft$xeKgNkygO2^a!T` zJ0lD+^R9tsA~g$Wy|&Q3l_Aq-oxQe$VM(%)4IR#F=OG&YBC*U^3uNNhRRU^=e@u*n zf?cq$>!N)+YiTI8TdUL_u|~RoVvUpHbK`FStZ`;>ipW6Xu>W1WD;g=WC6^n6AEs0DS$Ji?IPJ!pb z=EXH){vNul5qiel2!yCNqf!Wo`l}FF3|R*|KTL}N9!#no66FR(^y9=0^8e1w2nTXA z8i3r4A2W;n;MgU#9=XN>@sF1z5sClg8n+Gq!Zmt>xW;h|Xz+BXbPdJy8q-n?$`FTW zT@=a`(q%>nAVW<6H^L8Os3-npsPQYFAySaXB8dn61#9f>|F>A9kr)f4B!pxcwdAbT zszPGC2~s>+BvA)8u8@W~URh(Hcu=@wO43|rGKz$R1XeO?B=lrN3i&H{itKgxY%(at z#2m7)z*s4T3JDoNj%For-%BgoQ{MQ?%=qx1nHle979;;|X0-TiX7u#8>0^%08xf4P z$JJD)QpM_>CZp!Wn{0%M*07I;3R~yJ5~m}W?8>%?CS~9X6p1aytzS5!i|zFBe>p+4 z!Z_7kEoK*h*WUnVmC_nR+ZuyHsn#7sR$!ebnuO4w0G(PPR(0~+%>NeSp$vG^@2Y~l)E;U zM!WxnYZMKoXYT*d@dh_{mqanovz6@Cbj%_;BF$4@tjHfH8*+v&zktEyK4x_CNYQwI znDx`iU>N4el)Pk<5@DWJvevwjrPNk~g=F#dBk&+G1^ zdYKn-Z5QRvr9oBiL7?jW^524u{ha?3urU}m_?dt>j&ppuM(h;NXKM(RGB6b(SRe+C zxN@=^@1%e%u73BT3pGD?25^SCJu3S}_nx)t(CH~E)FxB{^${H0-zpTx+tH4;u15~OBC)nSgc<4Q`$lT9L**!p?Rh`e9hu+$G)9I<;(GH@Z4-F0hv%+e_=BcKUd z_i!s;t1s2hx<5ZRzr;i5yW6rsEFgU@K29?(GU&LI2OjCOksRV z#r+6o)*m58(Q2^`TRQwy(E-%(KM>=%ZEWbfgfiq*B9NoenUiRgbAO97Ip$kbou9Cf zNO8PH>mYU(X0?`Z2#UUqE(JtHr)aX88N&4Pnj+~)NHR`QS&X;z9>%B)0$zv;qOQKq z0F;#mxRrl&cZXHEVyrU(i+G^8((~7>FvzgO9Vtj1gjV6OP(7)%d9RF8XA3`XZXdhg zP*27v1RR74tD$Wcmwpg_!}3`IU3aJpkIGzBkQt_i%{MibMYBmzF9TL=a98q2ZLVxIt@Y2{tPKxqkkQIIP`z7;Bv9t2_!M$;_#-h!IH(BCk7CEh|X#quYHs}jb@BRbXs(lQb(oC;eNEWWXLeOIDIA78r9ore{d zi!=Yi7i&d6#{UKwC;tn;$opRbMv315qbmqt49U5IxrMzo`B6u4D`vLotnVX2lVHv` z>B~KibgN}mhF`qn$xuh)v_%|`AwgF&THD>2QydHorW~_Z{4G3j9C(^RqtINuSRmL_ z;NOVGgEK%Q&Z!cfTs-_3)&_XZ&jp&Xy0K!gUS_pn%T=CS%_@0Y_Z%H#~d-9 z+p>91>X3n$xo}%FTVMQ1WkNBt2)PUgtM3)SiA;W0!7lyGO5yr&7R9(;U zkL9+-aH?EUWXd5ypDuq^rvIPF7!{T^02$+e+>p`Ehi5TQ&<0o1J5j@FY@I}DsG0P+ zs-gPBpUL4$;i$)l$mM5LF-s=+jOHsZmZnZhbQ92)pnfpW*xgljG%Hfsy)KZD`UWxP zS}|HxB(dbf_lT^LFha5e@FD~NFEWAPMFVUVdcOF|Hpfe{ILtlJ(B+~t2rM-KQVm~ID5dI=%hT9HzW_5vO5qqW&%eJmM^ z<({3s%n;4aiL1)MCi#_Xx}i zSB;h4$CiO*RuQc%a7|7&)YwkAv_80Jln*_a$xF}UYkoR~C~=w9GqLdfhZtY%lsEWH zZqM5H6OMnVz4kcnU8ueJb}G1fBvClE;~ePMKJBSo=;AAu6WHG*8un1_oW3@>;j=aM zz1GUqvg^sZ1lB#lR~g2cr|GJp=E&OBv9!Vg!YU;XPw&s)t$Ws&N|s-`r)zQ5l?)HL z)=gi)lduSVV_oxw4dJr$7(jkF@f*RLH@j$<%0P5Ch~wC5oZ5Akg`vdDhs3vXVY6fo z$`r8N`!aFx>D;|7q$eFwxgb=Q!RS7QK=FWFJy*4W4NYSef^PH5@4lJeR$x+m&&b+_ ze;&(&loN;NJ7a;wu>>+J|Eb3vfwTHnruo{j3xkX-GmR)|Ky>HKDAOausDiSuW#fbS zLI&0OF!I6dq3(##upO?0@RraWtFVaUR}9U)v6XJy=* zpI^01IIp>wyYTKyN9V%xgj=09RX!X$t*9A&^t6>bM83{k?AG7sJ4i=b+8N22Xu$`9 z)2{~NRy%UtXJ;C;7w|yA>CLv#ghl6SXQxH3g`0P^UTpkMLjqezgj=x-+v$YPq1H{S z_NE>cS~X7?zE*HvH!@@%cr{$Q+E!gT?WLSfNs8|^%PV`N>bpkW$Gi7@9C^Kr{nWYx9lwekCw8f0hP-5$=g-E(;HG3zq44hKi{PTUZkpT@-#gt-)y zPf+YIZKKvI#b%~91_^}Q$CV(=p+iLk%*|P+wOXe5`kYsd@zdVZoTu>JgF}v|zYC{F zIE~|NxNt2@PDi!3#PB+KaM~Aj&fVtpw6l9>=h;C2<<3E-;(WyZBU2(S!gm-vGdA`B z*N>3HD^ZCvS|JUl=5+N~(}>WA>Tz2xmiY|o@>+p|{qOGhhT#rejR`9*O%r+*^rjCA!s3wBv=JTbZh}Q zKsqJl(q<)Ca6lq=%{$kyJv!A=TV?n6th$IhFAdx!gz6TN0~>ydiCM zW~XHj+ntR+jSaU|9L)W_f)92}L~B3h(Wdt=U@=Oy3$j`}yS@ zw7hl|(eS6TAej*L=i9ecUxtL)q^L-<5PFQL`=Xx(>6>y`yR-pQ z07PDh)a9OgEg#2JwPoj$2)yH2i38k_uSUA^h($~W{mFl<2FBaY)ew_o<@RD=i&T6G zC;4pX3}AExlE#E4@B71K!E(WJ>7wK>r!IoB7PX!?>3rqzksmd{SVbfz(3DD*2X>>Y z8UV-W#L4${F(Vg4r4|guXy=uIj3!B_2!{frcFJ=&kN0)0=ZDsT*XciD%)dGLa<+f8 zeo`I(6-;ro6GJ1h&`(ijysc}cAh|fc$bO35Rw@MOPecFL{glWcTV996DdIkg_}#$6N#9_hc8Gp_zSIpeL{yB}vI9;a?c_wemT=pykRLsY&j zL$|{;0LOz$u98{l`hi~HD>mkX%~-Q?sxfclE0o|Apd3uvZDeF^{FT z)==`I<4Ks05T)&W0;AJF6OF#6sli6q|Fpi8?ca`&KGu ze0~#twe)&yFXjfi?OADP}`j_{4a4 zUvW0Edh>Mle8${+JL&=`Wd+>2!th&;*eTcyf*HjRYV@dYx@<>J_jLoP-+%}K^Mij}^b6&^n|rw4oY}fqf`%H2^lX)S z2gc?h*WrvXT$3r)MDvRCKNB2qmR>P~G{F5wyQqTkc95n>v}9e48C3Zvyizm<<306S7g!YkWlYz6Bc! z^hb))Y@&l4i-0T90{80k)!LNvdDYp5k|tyXf>-O03ee9upCl1SjDOQkn{R7X1R0m0 z`JHjwK;YvTuwol=>v3*;73yfU=LEn^@juE13h zRNzf{4m>y%;E3!-S)U6!F9|dL;jz_N4*qhKHNfReCTMC^ zuvZ9y*dHz8oqio|K?Er{+>=6uJ>{{2=o~v<(&<4zxN&$Re6<+lR-#E?xfz&}*y535 z9RzSR5X2Ya;P1lEd4I7rH<5%#go?s#AB7rDOniw=ABc7~QJ^pE-zi~gN}ic0*msE} zS&o_A2~Ix40#_7Brh@~7q5(dXcQm)$3i-pehuxh7c$D{e3W!WBX(MGrb31f_LUSwR z_QAki2B{W7%wr(~{{f*z^EgmSK>a_78N**UXYF)wWR#h`yMj885t)8XA{?2#qz%{( z=g@R*lMR>*e_&5yxR-zdAigw}Bm5okpMJ~l%lqOF)`ep&Zi46&tp6rK-2x=2&3-4S zyEfzI?oUey8DQ!T+NLy@^bz z%-zmsZ&RD{;EXU&9lZgZ2)Nk|QMSme4@vKZZ6DU;Jkc&DuF?vqLM-|0qV6G-AQZ2< z3VM3D%8P7Ijgl1`^I%rUdhNJRnai`E%GFAVG&8loA+`7MLNcNmtyuE$O z?1B~%%_jLis_cL3Wi&nXx|?6PvTbra+j(&$o(f)d)tx0bguRsj%2(QKPQA~aiew96L zcMRq5eW?0VjMVcNBGRwmg@^)}7edO_9q$7%)GN`500;}T>q1*+TL| z9zuw>Lu`pZk(dzC(F*mFA6L=FX}xtCN6|7x6_w#n{88^G{ul+qAF2P~kJxWX6UtRh z-d(JJyliZ_MinEMJ&bCBSYiRRj#@vpj#JD<;i*Q$)H{p;pCYK=J!&1Lerp|{)0-=W zl!SqHbbj_LgrEusSYAqt*H(!BT(D z!EYWNob?7*u&@phvCEf-r_hlEmtqR6WRtq;9EZMY&gYOBb^=#q{*;fA77-pp6zsak z#*?}kK}{C0Y=Pzd-c(U=Wbl?4UcP#)Q;Rb*)VKu}1I!`q-<^aQ6ss_#nHUwTw%Y+DkyDAPz3r z2O}pV%dhGAQ0;lI|A3|t#Uj1U)S?0Nnh-2MP%@8Cu2STf=yBz|MuC-9kO4NX8 z5%1>YjLgb&-Ay%-rx#QZWto*S9yHZm&%U?8f^4SP-IHP)TZ?D?OJ`q$C<#?%e3VY4 zHjcO25s-|eY0%6&%~KS<&uvQ60JY_}6nT24F2dpgYLgH|nC_oLktN!wEI6S6jxvR? zsMPYE#$BhUwb!d#^k|7p{wzCl1m@CcBG0UZ63fLrp^kPH^4L&zed+gk5LbL_87^Jx zl9ZTKfe6`pNc!g4qX*xc>)jrlz4m^@f`3@wxuxdd6fqY!`M95{JJ3j+FHT^fZ6HC> zHtXS)8O?Rwu2a3b?d_96PoV|A+C%p}BTBe)(_{jXY-4eos*PF7Nv6gcFe6?$0 zcH-^$T1y?6?1|F<$J<*5#jynpn|P4mE&+lM5Q4h}hu{_<1b5fq?hb>yJHg$Z;K3od zTYvz;18h%n?|t9>s`k%r)&8Xl=s9!xbkCfppGSg41s*|?OqP}-9Ih#4A)qj}L7P9n zD0N#}gDsB^lCs|ZH)Xx2^KZ%;nC<*6UYaMT2M<$I$z4spv~CEaN&mSW$B7m20JkId zYGV)Kzi!6?!0m|VZIgqY6W=m5JU&r=QaDk7AvbA^0Wq?#GNX_5xpQ=GIJs~*Pvn<(BZI45GYz^7 zt0&219#9}RcTZXIFFjKaiPHOwi-R0}fE7v!Mdg&(6pcRK;*A0Y?hD(CUyOQSUik-S&!Bn3!L9hA!VZyq#M6;%&!lsh&HBQt0+& zB`ajmtqxgQqm~w3U0)qKi=bTU(1qF4!|1E?+vsLerGct+L!)2h3$KTQ8`DRCJe&zlRJ@Ow;CU?WMW+Z9Zou*- zCE6dSkK1kISKmbB_yuE+95wm85locx)@VLxy;ek+hs+GJgqVqwb!x8j znEd#)fAldYk^fP6cMnGCP2vuCZraDgEQ6~&FmZr57kLs|4X0sejj|*!&=+RdF!Q!R zP4;U2LNO@lFaEd}LSJ?IZF6n@``U|NA7ILij^OAC)Nt^TQ@6HaAap5L8C7KEIIJ3h z4Y@j>2<}%c(&PTRNymu&a8X=jhX!AI;OyOpb-#LVdA|A6>D4RPvYkU%dJ;8u%m!o{ zA@&JkwS(SNwUphm(Aw~N`emr{#R_Hbc4Q+M=<*a`2`Sg!5_Ruy{CaY7YO0q=SUnOi zO|8n!qqMq1s3`L#JTP^E!V3J;u$_J@PSR8o9Eg#*WE|1YK>vYVYZq z7<8kQ87VM|3)mE6oo6N$M~0V&EY+mot@n=@aJhBD48Tj1U#Vog#TT;=H1;xCOE_WQ?6Yjhf=3P1kor24|3dsQg*5 z>r)S{q>yd}bh!~xta2^;o4k}XqRhV|T>dR^0O?W>*TT9?sSUZYQC|<2FT3g17l+YL z%~uU_5muY8L_fSrUyxk?MhK%m9Iqx)Q^ynXH)Flly2NBF*dupaptXKocC%s7WsN~N z%dg&>@hy&im_T1K^VM@%B8@9Hkb|4n&8RDFF&R1_ti`dh7nGvOO3Nazj}gkc7G|1uu@VX z{A1*s;2xdSc`3Uv>8l{?8w(gos{ovJ)|Na7hPLyCOf-AXaJ+2`K3)!lPx z>W(^*`8q#!EXNQnV<#%hoNXN2RIJSu?t+S1ez`TZ-VW@}yk)|zgY!CC@imn5 z?js!q{5%RiquZ2MRelK=m2*tb+B3mWyY^7OQ4g;AC3m%HXdNGQCI+_Ps{Va&bym36 zCQSDM4&LIHznC-KP}&C`ji+E%%2;TN zzm-|eWh7dd~FTgq2p?h`Rns9cnxtn z&Tg5RS=x0}Rc80`aFp4cmZ7v9+Jv`J&+d9;=e2%dOVk zbcToErtw_+QQoUO4{xt(fxBh?@?BO}|CURqr#<;7MKm3a>^Zv~GCsSH-z^qP45|VW z!07{6l5#M65gsM_Qku@h2$K)}tCOY&QaAmF_>c%SEET;Kjo+$}t!D|#9*5A-(Oc7r zyVMSz&Rb^}BR;OAwZmk&&iYZ4Rf4b5bKko2AmC$XUq!G)iie;3p8YLroyG6xlr^Z{ z3a33&KZ58DR8r^3o0YcVZ{sUm!UQ#Rku!EDwU?(hSIR9(61JN{1-St6cYy`>h=meUMnb=MT#9n?EsP9nCGcp#bEb>g?5n~PdJco03Nx!E$!`Z&q~oOG zq`8!SIB={nA0x*^XN^yZMi0%Sh*Ve&O2vJ-1C|fQ+dNP1>pyp5a_Wl3Ub&8a9h_Gth7W@9i|~{&Bs5S!;(aT zS$ao8qnWU1BeLGP#TvP)K5=G0n_;%Q&s=lUfjJR*%RzGLRyl`pwuW98V-gflF13 z^ary(vPDRS_-hrn@nfB_3fPop19E*I^E*21n>v@H_@ew;`vi{E#$zmMc2xuM(vx6B zHq)}0%SB$~<`$^tmUexyn{6JF(-!W2LSv4RD6RlF3zYXnk&2(@uI0ufFhjdT4GDiZ zB3Bs(@*G(O=nr&#AEg@9a#RtlmRiyFO-*=VIcTy!9b0sAg9@gp+FRiNk&3${^`c?v zO<}zeFHHSXcTwppR}#u`xdW`BS?{L49BDC%*~rRV-!v58Gz@pbTB|angi?;m z*W7clsIiac{KE(-ym;w6xzN~%hCO}WNgyi;wQPkoSjrqNpRj^7nFfpu%_r@V0Ji)~ zM|0sB&iEJa;wjcv3c^b3p+SCfDtZPVHZDYH9L1yDdE$B7VOb_r5%~l!lL$%hcF8i2K2;-?#mL`!mNAG6xNgIYr}1ClyX)$=YFQ zI1uGiH>&h*GlEDy58yo6{|QwelhH0RhT-N7Tf~{k#%rAuPE*ljaCg$|2gcVp0}Wni zJFG1hQEm_vZODFNOng}0tbM}^rG98yXxhHVH%Ri|+41_NDP9>73`iGw(p=GQ0-DE4 z^6f(D*RQ1Kyi=&g>ws)vw+?Gcf)o~Cqmw)%;YT1;jSqyX7g_tk{M7GjnI{-AQR{oa zaim19R)Gc=&RE`YK<=YlA4;D zO%{6y=6HS#T*HB_=m4#Vx5D|ilj)%2cbFNK9cHAmaUs(u>ZPs zGKebQ5~wTRk+&dhf?vlL3U2p6&=Vuj3Eh5=(HwMm(ixcGwY)uEFG93 z-zmOlh$4C`41O;OVIe!!QKgj?02Y$uUIa-|8B6Rqd1A9t%%RKiqg)*D({}~KieSgx zSm^W*8Ae7^fJPH43HZw;bjG}W1=AT6p-Ci}GYCpss!|{jE*v%xsUETOdEc9W7V&`q zh*SfD#~tJZ=YIr`a9i<@{;}i#2p)|TH8CNA$H@@-?PEaj2m=v32FoUJ6Dnbm#!Y^t zAf@9r0UP8YHMc_3K+_b5rHY}E*+o(XiN2=;I6N$oCA&mA6D*Dp=_7jSEG82y`!(fj zIj3>BSVl6ch1Vvm7TL0r@!G2ECi-9cs73tMf5gX?4PF7FYm~8`eN-tVDIU)nqVKdd z5-KHf6a9kHEJMxS5-zY0RJ&i5ZrHU*CG8}|gj;W=N1F4WCNkRBrkt^>9iXeRMCgXH zE76njCkGghm`8TFee*fwNQiJRDPY87`%5joEr0*|&P^4!M}>=JZ#WiSFfv`NMCwGC zo;Gt)NTn(+!jZ;G)8sQD{dJaoz;ga28@!9gK3J6IwG6#vDPf3!0=cGca4odC!Jcqy zi9>98qZ{kQkA3asFMTOt=JdzLMz7#Uw4p`FRX2!puoe0J%jhr2^unC=QjQb`mUPM7w&n9(AQ|pGYYo*-IQkEUrMhgq&pZL1dI+qxdV zqz|30dlpB3b(~N!1RsdonI!W%UqB6gbsFpMB#4OXcT7&fN$r=!8ap;!(UY8S!6&dr zvz}`Y75}(5@RJv*xf!Y%@pW8&!bybF9ee4`U8j?%Q912g$fu&VN1W+NDX<;E3b5Zo zPyMl)t;j_NUm)y4c<{D=ERZPdQANSPka2{ShXX4!EHmy1A?$m-bkE6Hb)4B87N%`_y-3BibE&Q*A&_&Oe{g;^E&Jgx7#sz?;% zNO+vf17<31kvWI#eu$sNJSK{x4V+NnceJ5)0=d6VICBuX$Y<%UIkhF@$5fO~*U-o- zq!kV}aje&28-XQ$WH~28+>a=6vMNVEh65=4$R(@PL@lXH1+iF%^LoTAwwQO_KCNoB z_ToP>mby8P7CIrB(-=w16~6Ie$4`l@%;*O1*6nbGLd=h?BH9}r2AiGJz&Uka>>#s zleqJy6ln9w@K4YR@wGa^$T305iC`G_6J}MWPz=gq(M<~Q7Nb@~QVB2U5EJ&Lf$F%r zWx8eZyQq6)La2;n%lU~Vxr*VI5>iBjmG#@4*}c+-Q}?zpXkOLKIzo@!<{=5UKnWvF zC1U0n4f`rH%jevVA7pU4vMP~NClN_kL}g?BEm}+4)z0Qf`xytcpVH8Uktjj2Brva7B^#v}fkRYYM?07`7nhjkr)K`Z zdNcH;L^O;o(>-=)EDoB3Y}v?j3NB9@Dlf2FI@_N0Q+t7OYycZ?F>dtB_v5qA{Y6^e zf)tVHov)$9_rxFu$O0M)dme|6%J5roJQd}Fv9ohob5xn>d==G7TwKnIKvO%R)_`aE zMiBVZR-1qm7z!NdIvi@v`bnK@GmZAGzY0ALPUid<42eiT2@GcA<}PFXkzn)H*~0W0 z`L6{uUpdKXm}MOdmie}aSI}@5oiO7?>UVdetCD@eD866}7Q%PZsS})GaLNRF4o(v| zL{kcDzs7d0js}$I88H)Qf2PCAGrf`zrrn+1pt3Z}D;U$?wS=eTkx(sJDsVI`dR4l_ zQe>oRAUWvFT4t01+A2w9FP6=?oklX!6x8$v6p)CTMSm5L@&76y1OHJ#w*QX;@}dz? zK<2oA`NX0O+kTo?N?;ghSl32j?`UtLMhZogT{;Szdz}!bYTl1VnD=?agY6wp`CE^* z{t8fnXbc;XU09)0^Zbmswn1fLe#N9VdHtX}^2=7Jm&mV^F%fo$2@+Jq1MifY+6PNH zwRidBRH+BkC}rc=tMjB*7@c8up_4Zl$vQO0yRlGqLYJ&)V?vaINY<5+x|L-L(o8QD z`?ZOVhr4T!$dn{|?>#HhIg;ebN8!oeBlfUo$&VG_ahnW3?glPL7fVy+%x;dS;p3VC zd(=ygX*S!cVlU%3uDez3Qy%Gi_D*o5lXevf_zBoJdDwDg)9GkHY!}|MZhdL*io~Q$ zU{VPfIMY*PdtaF^og3MQXmVvfWHe(f?gu2-3}7!hKrdn$uu`hOk7K6+U`pPo33F;1 zHf4!+Z#@8}yuX&rF>m>y{@yMmX00MvebgIt!tV<<^iL4&=9gBakyL>v#!#*f3X--?ens3@ zXK!9zlZ(|VnpE?OxIpYmEx0k5?t4G#^3aU81k?HOL5s1JO0vrXFv$&Tz5vyEtYk=*GB4nPj8>d!VKy`xhcjTz zCioEPJA`_Eb@DPS?#ZWuLPFM5GIA}9+0BUCa~@Au;G(C|W9CxW;906^q2tH3Z8(GF zYl1!`iXe&#@?@2pyDBg*i0X3;>(TSb{bT1ZQ^JuGnIB)Wt zV288w;i0u_OviKU(4&icwdtoU-6fO>!_oYsw(X7PwmaswZ2d4}-Le-9c>HIdFL&xa z+TC1homp0#$Fu96hj@&$ofU4{=8AQ$8rWApO!9uHQm9`1xQ_Ldpz z=-oGWGu7m*qLj6OYzN0Kwp#?x%D;7MbD#KT+Sq1p4h%ZY`{4;~jkZ!S;dI<8I! zv*=xDBOqRKk3ta<0&?S?#wUoeDlb{=BS>1%t2~x|B$)HJ^u?^V zo>P1$Sf6^K?1OdPWZ)R(?b=3CezUgWU)5F7$?{Nvrj7z>TeBqMiX!L91sy+yESPV+ zw4j!b99*~(nO(vT7X9F&OVAko12sUR=Fz9MW1YlH_q4UeRmF$-^WV$|^wQUZ+Om61hKn=h2?XNpve}}~&WNoFkHJA&ED;WI583Oj! zRAqc)8-*e#A$N(8Af?tXEpk`o9(8k#0P8Z-QjxSBk@QyuSyU?tB^_+aY|WhuB~5Z_ z%obM9PxG3Nzk@7iXJMo%Dsj%Go4T92ZW~->symDS`?HJT;{d`R732+KFc{br+}!?s z_w;W4(BnpX&yWi9B}y-c+B`^yf-o!h4J;H!7>_nd`iTrI7TXDCBo!28KHL6F$`6tB zpF6$n@Ai`Zw5`uD^{5ai`xZDYo8)vq>84E?zH1uac&Ty>X#vCNp5%~6VmOtZGYOF zK@yh9iWTWd|Gl9%A4^e2_DSP#+D3}7ahzCLnI(J-Xj@y3{M)u}b~^W?q1X0+A%G_^ z_KtL5NI0^-T0CuP;LuCpCk%2Gm3r}_&ICuEZi|D^c#+dlqBJ*C3q@HBEtVpPxoHNm zK#H1?5?VO?73dRxp&sdcF_bK{Y${oiXRNN!jd%M&hR^*sSlMAf14*awVjoCbmyZK! zYX+75PV2oCaHz5?8YFFPkdRmALO{1$gB=4JAj+R67Z3UiPY~etjp{ov1#n_5TCz@K>lwD4; zNz6g|*8XmTk8E&$DN8l-{03Gg*PrbJMf(1VAoo%h9h+OPACIeqx)xfGh3VX|WQKvj z^}XeXMElnxUd!!-)a{=a0EQA4U?_>X-4RS745eXaGi{iFL?OUXg8pSFEhOApnjVxZ zt=joSGMSiVvLFm4TJ&0#EUW2145ffni){uMz)=2nz);Df2Yp#qK>z~R#DNoN{|2rX z58clH4O{~|Okdgs`HgkY%O@E=*k1dpUlb%d^eNNPq|IL5MFOc}dTurEm=^0s1I5>0 z8HF$caQsto9iU?kkO~tf=`fODLJeLTT828fEj}&@v?t-O-?~5sOFR$=T(A8JTpzo8 zNkm-^;$*3Q=0pJk*IYp0I^(~tW6MTP1Vig8p)p$`Fo{K3c-!FGndWsAfTQ*zpwp7Jbr1 z=R79oD`)e+D##UWKlW*1A8RKBAaJb#30zPA4O~Z}8(Om0)=Apz?AO_1*}miH)&!oB zijrO9_$KAih#CJTk6q*)>gNjV7~@>m9(QfFSX|)H|Gp@C!KbT-tDd!oe4oF8YY#Xg za}i1(^hVyH%oRH^>1k%W0$$j3@Auu99?caj^r({L;kaf-o&+EBK}{}*dMD4!2cJc1h$^I#lC#W zY+qjIP2xKi@)hl--|CdC4ri|kclz^bzsL)MQBt(i6vaLfUgH56CF+OszZfMU+2B0J z&h==F?d@avT;^II06l))@oBU{K0nI5jw$quO(hUPq$YYeUA0KziWZ|H$dLJhZ=Ei3 zKXM+a$A(U$%#dPlBrXUQpn)xdOI9IsBvJ0q0w>k)UN%cryV}C5^Xy$+C1j5DCvxop zXDIbj@IL-^PUrW}|3$8$5?)SL|I&h%AyEWe-m&g~jgZ5|GED96O=~TJKYnB1A|IXl z&8F^?`bpviND@)~TDwG~`W5XA17hz6g4o*%iD{D2d}q2kUY#vo_RY(%q<(ssHv%v* zjEFJpCGxQR%IT7_BMdQ;-s)rq=A<=TK|F;O#WFy7`s|K+dCB{{ z*5emooJ82YQr1uu8SbIl|FLnjOCIi_wnhS6yK!oTee;g-mx)_kaiB&tXi16FYsX{@ zp^^7CHPIk;HX-4=&ax_>E%XPj)|(el)C%Wd|4;mpiWQ+}*RLQKe|Y3~xpJsGyCQ2z7qDs@W#tF6SSXUqSp6CW^^lF z{+Rv==g6E5l?|bUvVX+AU9Qsk?H(|&#I+I64{J2~w{dN0{1aG#$7#7UJT)=?qLc)G zP)cA6eO4j-Uz8F!A)$8Vt8A2)Qbn{wL3oW^#JG5&tc&D2VEE!L7V4jd-~;z(S;d^ zh)5X&29$6v?>SdC%2n_!EfLC3)mIgmOmg%-=q?MNV}#DU@0{QZz~(eDEi5mi1!H9< zQu%Sc?8;JZo<3P$(j-a|nP?WUZlD>M_YX=1S%gXx9{1zhYkA8e!=wXPCQIZA{P^_a z>Y6P@{{t~x0l0WHB-r1?eBxalt{3^3^qcHS;IO3Ox#Gk@0jde0ZA?nsLE&fb3hZoN znLBuQy04=!rI5F-^jqSw&;G}U^=Mn&HJH+!!^iUv8y+Tek;4(HaCsVAS~Q7DGh;l| zuZpT0SqjhYm8Qo~W5Sw2#=$bsMT>pLQ5NUwThK|AE8 zfDJMihb+S}9DPBGT>E{mH84M=%qce2;R@*3p6Ss*-^cu5rD}rT<5nfWm{Fr z5UuWlcgK3mr{{(q7U13vVBX$fXYXGrwFXvCJ*YI2keIL>^iJ67NPu|o> zRkF+Uz*&KOK>&^s40@57{epq)Gga@3ggNu#IJwwQ0~%o)3v^4 z^#vx&sEm{N;^JFI2Y-r;ocNo!lK>U!D}q1lX)xF^2o)sSvJK1>#E+gOjG+ZkIXkd) zz|CanoWlOwo1a}|P;_p8BG&===)TH*)YGJYBG>!tgq-6*tAm9@K4Q`TR z=NoM08g~A?f?h^T=-6-p?SU7CdN`aUFo6DaEJigN?Rg8^smAN-^b`ZYcC!^)I1mWc zr7*fCM*uWaIago597|d}y@p0f+3$DOxqQ39MPWQLjwpg6OGpieqFd9{UAxES<9Jn5 zCO#Cs8?Vm@s{E7|mKH;s=^`sIF_m8|;irP7Ye*T8sI}C;MI*y=*EoyIm|`Q94t3K& zI3F4xpmE*#x%2*Ru9#2JW)$dC;{*_qbm*o^P5cR6TL7VJ&T$}gjcuy1286By#51Gk&54o_t+Pl=yO6bdehenPlPHZ?uFUp-FB`BHWfZUP#}nxg~2 z^2?JI$Uzz2i_J#HuXD5*>4U&;Z*@AbjTE4#*^O;T@Ubs`W&xkL&5}Ew@u|`avV=1V)mg;J2`oWsu;hSk2ErI>@oNhqsr@wm$3LZsqG< zUMNmrQ(vnVds+geAlLCS{(vH3+RI$*@&dz^23Y+XfGsV1uhsh*nC1arv2!5*J0JUv zpzf>3PHvGdULcgs+K`F!ze?BmA@USJ=^Euv={o9NIJ=L$FOH;LX(6EsQGt}yDzp@Y z9+WzKx^F*s-~DmBFtPv9H-TTp1^K_!^&XJAW&%>zv&?@}*YiN?dK5@q^Z(xH26clR zw-Xv{mxY*gZNGSGo`{6oS5}f9ao=wob;CwvjhrRgy^CC#bB5m zoKan6z>!o!ffx&6WMga3!+&_1`h2jJe~SYwH-A#s5UgpM=Wps7*K4*n;bAZ}V5C56 zG+s))QI7uch|j+op@5aUPhJ&4xhCX2?tiIk2+4IV=q*S=vhz3b;o0;2j*pMP zhtmoXT>Z3#krOuvx)&Qr?Qx-pYl6EG20XgMvF4}7m>&4bN}H%019c$A?=KSel=aF} z=+-gs?3xdw09<-mZ1z7&NUrh?8rC}TKT62KKT1g84!xuSp_E(woxTfgHFEXHZw&xK zPVXX<277a zBGj*M0H+zoxV~G1ELoC^AF3jyN_y7+A*$B%Er!?v%wHI=I-p#|q5jGGd4S1m%H#;1 z9wLRjdac9Id7W#&R*wZ5P^b7Kg)D?fA%WY33$u}brI3G);_iHx@8R+O;XC0m)R&#Z z7xY~Cp%$f7$rVXY%c2lguYz$6QOy;tB(H?}hV&gSH5`r<05sBmF(`WmOpuHjHwYy# zC9FcKnr9oQH$Im42McfDRflJhRGI&ELXtsewO57xb;<++f z=w$7{_TYgGi4JVaj6A3V4c?prI*$xUxbrbCKM|gJiT9)EK=EL4v|#LEjXqZ#5DvE+ zfNQ%+g}=QL#R!~$fiSQ{UoyIcyy^x7t2~zn&-~O`qa;C`pq5^A`qV-$#}M zoRH{%6S9b?pb-!>-p*`qvHv%Lel=RLfSDK{;@(@?m})?QK|G%C!|T2s%Wl_yY&oCGrrg% zn^@}42BK3&7mQ7g!>pe+9RLZe`C za80MOM)hiiHy}6_TLxR~?kum(%h}t`?S>!a43)B0UzKsYQ2A5EHm%|n!1DjO;q zGV;-$x0JhhZs!-j_D5&(DZScXcXnn9B>m(ul)k>KC8~N)Y@Kz|YPY{MFgFE!L5d-< z-|G17*y;*sU30ir1FdW30d2MHUUfbc_(8lry((Ets^q;^$8OqYan2FeP)$nLL>C&C!+wHvA z{(jPSKgFCIs&dray8VLWir}8`ECd}TGBA;3FBN~g1iyWOh^?2Xpsw5^b9v=dmH4}g zcdj{;*V-2*;5)=$5r)BuYJO!Cuek9BLJa%-!lGLwz^S^7r|b5!`k9A44dm+F-W@N# z^Oc1%UR*d7r#8DEa=q|KdHAtd#q#8#=pWl?+Vlu_8uwkG`HNs%BN`LexR-lc`a9Jn z$NbYTFV_##k4A^S(-NNjyhfjB8wy@K5U#`-0u877zS$BvYKwWS7gV( zcDlI?iC5stuQmA}b(YurGa}%K?`MhQ{j3MDk2c`e!53Cl|A@4$W74r#ij=omLPV7NPt z0VH1+I6m7)pMKRM_9*A3)wbLV^&_t5TRlauw2S%}g^4`zh`m2FdVQGa@zR4fGjn{R z!M2yb@3Cg+nI#OdvJsm;``#uiE87YkmFUXWfm>VMUGyjK9KJ}Cd;Ls2Hom6da_iCK z73*oDOKGdm+as@zEEn|h5956LA|2xE$W4ij84ov_n4@m~Uqh6GV}5pdKBqO?+#!s1 z9$Gt^t<}nR9AxQ|St&emPFnDwX6gOpx9-^}l|u8@$X>PEdCh0H{M|yM6aq++IEI8z znt4Cm{Dhukrg9y(O*Cl(4^79NhRwV+dYU|z>?>y7&NBHlZ1ao3_yc;aL zwBRt5YIAe4a zOyJL(Rd&fLi?OG@JbZ&qk8j9pERsA0(n8c1T$M5Z*y$e#DzULCP%U9GnTM9uW{Iut zs`pF?dl=@slAcjo!vqExgfN}G(sEL#>O8P&^HX(n;gsH~dcBq-HY_sHzm0!c^OhOxTU z{Vq*-VKrE^Kb=EkV>XT>3rg>q{>aYI7>8z4_tB!iu<<)}wS|R|c$|c0ya9)3Hg^vU zhqxTYUPCC16%&+|$d2*X#!?{lINuz+rP^o4WAt_`-p<$D!>YTi0R@Hhx?WbFqYUU7 z&=Ax_&g)RH%+1{dyu%as--8(|e&sF zl^}_W)0rqhdlrsl!|lCxq}i4o{FIyN$YTC43yBS<`wt6=p)ir!8FT0BYmE2K$vRM9 zA~NSHSv;48Z8cvUY+PDLi=6?NMAEK4t^qcjUop|NuB$tbb<8UPo~S@~TR-}cO;awO z$0BE8Ogl#VD;8UxL^s~CcX+WCiMs`g8woS@bq0=aeBb5B(6E^OQScsw6}#>~JW|I=>EvM6WkpfnzHqkhU1&0co$y4Ruh0zDt6M zcx3aRNbDrH38XJl_k1lQ!%m#DQKTgnfa>+dy9>6!_rbzo8c6jz#W*Px9+^`bd4io% zFHZY2L=egTR}e`cCBsV`L8TVWUI+YSOSrcrDb_BNNEr{d9&glOnBGa@bN>m|4 zy}A4_49~aSE0$Z)728goRZ+?S(8W`^QIVu^`G0PeD~eT`p(_d?*Hh zUo^_=9}Wfo4E0>YIv4I?iO0c~>z+epMq=iX8>4c#^);jdk!Xp43~1soMuN-5SmM!E zf0iV*lvB91rbzd@yanOpS(E_D`^g3Z% zVIgERV&4ESk-%+nlNTP1&f_q32YO8>*F>h7ss`*N=VFH`9Z{v$AR5?Kl3AiLZqSIB zXpfvN?pAGVWg`DR8Ft^!px@{kU2r%Bi?&4A*ds0=VYqoaawk$c6ewPs4+iNZ9Nwq$ zqDs|$*Gk*>xWTS9)%uk_I+zn?UY2q8RT(*>1u6q+GjUtt=0>mFaU~ViOhWw(gwb<8 zED(m+EFe~*gDlhAB0b*Zh!qiCQ3Fe}C0kf}7l0R*8Z?>9&&WdvHA<$l9!_ zy!y<+ZqWL}=}QVTQhqe=3aEKnzaYw>5X@E};}2&&>`ti3;YWw>OZU3=qU*Ip(M+an z$6@kOH6!;xx`RHA`gj07yx%YzFD<%iMpRS!h*U?#V8z#l@kfL=tfiF3k~78b*e?hg zdLyv7y2bbOgx@mD`$aGf$|i3`y_%M~M9O(tl`0&uJF|t4m`&o-DqrQX=HgV$##^-Z zcyi_LaMNV{((T97xXV$f*gn5AO&BMs4e3G3%B;rG>@X||Wg{#~ zAcmdo9Plc2Uq`Q=hds@K?R_E;!@ifi#7ur!8Sfi`;v0d1$0M`cDTNP}>YjZz$|ct5 zuI#mF+`6pr`Z2L&ylp1r~9I$s09@-HXr32 zD!9QsK5scypK_{<+6qFBMxk^^VeB<~d^<*PsK*}UVeO$+Y$vDaiJ}AD)iWkk@7RLdXCqnD`*)}2gwSr(b?1e z!&NOpwuQtAsO%2{C0{Mej&XKKU=Pvmc;>LRM#H!;LAi)98|}BIMe@;3zO`*#9gUFO zP(Ad;PB(cju6tMd&Sw1PqRgDemwPSDfYMH0>sf& z%GeaC5 zMO?RGDT#O1sqAt*cvhIbH19J7-MXnt-bb&M2Q!1X9q1PVizcnmK z3|5ZpvG|n}Ggvi6x3%ud#ycR!M++ylKp6v! zV2(CaP9Vl8qGGL)Fo+=L>)W@O81+5zn7Qru1122cMO7t;ju@NE39gi4+WNg1)R#p{Y-0~c zCJ^TglP{ScSroATkxBP4+A>yu7561Ed{aWXJaD$V7tSHwt@JM$Ill*xkp*I;pZ=1O z&VR|s#65tFgwtT=E&dOTj8GDqMV{i{EH1)cJgz5i4k&0>ADm3z*hj6uw z*^FHxP{x6bGs7t=;|YVq0_$S>7Cx8?y?))YdJmSKI)T1OGh#|OrAjlNACr|O6g%cC z2|#co(JEWI^LpW>QZxa;WAp`s>dcl{JSM#N+|3_@Ni@o{vf=48wz3)mXW(e??#{WG1|mvb>3=7}F_W;{$xT1o5)y8AArrX>~0@H;=ta7rBAF7I0nwtl0Rd=%&(-^qs6WQ6Tv zwK>%0I~-_Fd~=a6N&3t><0-=-ktDw{0>2>>IuxnFC_e2f+uO6j4$M*4F)`J!LV$-n zmCP}1`GNP|E(pRy;*EL}p6~;262L>Y^Ggduct|6Fht&PULn@AC!2HKUqQd;gLlziF zecdC>WDDBdANJ6upmu_YC2N7Y948YhC#UK=7b(_8N>(Z*ub|gI=nzlw%dK8UI28>L z)L4LwSleY_$e94KWc`k}^X_PiS=Np5lyI2?FzZ`I%#;3zB|QPLj z<{~k!jZT144xxn#vdFSBPs|%2ij4w9vGMNK#a$lCv?5NkiZ4|~dj_U>tThO+4;7S4 zOX9MDhpL=XxQW>)bJ;m3iNO@{oXxVe6qQmGU|aS5U5mj88RI`v$$K6SrWS})QWUEy zYWthA3?P*p!&KzKH;JrB6^`7+&gV}w$RHR7J+P=4^y9K1v+x=Gk`ElF?AxqsygiCT zZ(L2!dV%#I+1hbx>|$EYs2MR$AddmU*$O3k^OW7O@xx`pg&)67x23QFi#U=&^EYo+ zkX}6wcZ})~sgOx34GxNlj=4joo$iCTqM62a;fF_(A^eHCG7k@T#Wx2NcOQg)&uze;TIrH%yJRo9aJt*edt{CVY?tSo_7MN5azT}2ZOh7TbX?^*aBg&; z@*B#iu{hks0vV%w=^-F^7;91XP}BJ5g1o==9aG*IzH+wVs*-;w_di364)*5jF9Aot z=OZ<1-o3kDKKm>$Fcf^VEx;}KHI$zEk5$tCL%>WG69U7#dXEVf(KM`w7bL$5pq|y-=-xCx%cEacj9xN$^tf5Msd~AP zkhpM>ncT23*8CX#r8nTeziN|}@i~SIU7?XFxUKTe@%ov&_($F8p?A~$YfctBcsX_T|mT*Y32@c=-$EU0NwQKT`U!z25LjeaI+| zO<|CVL0#KM-`3u-sJjjoj-TSR4QMn*yI#6Hy_UWzU-Y>C34>6=^FTmyP)zHlel}yn z!413TuKu|5yj(-Hx-Pz8KRk6l^>o5}@Og%(=ey)&)G3SETi50#jq+UTF-sD=wI{ta zYlN2}oa`xPkEB1Y<$SW{96Y+(9$o4h8FJSkVeBx}pZW@3tSMHWcXii!ty@>$hdx1p38BJF zr_BAS)V;qimi0%8s!&KrwOOH2KY~j%+)?u(bcRd;#THfW?++cS9v%UHZHLihxz}rZ!bfi!>esCutTG{$^dH(CC`t!phCd4S|6$3@dLO|HrsM%(HKXN}ENs7{HjbNI>`z%Zq zy;(b0!cNh{{(D83N|@^MH5k{;qwUe*Ijv%)6OLkd2GYfje)GOSBKEUoH1P_)zv`k) zsQo5{C;-8SbWtBK%c_nBvi=;lvzkwgbhGD9N(z8M94I@`Y99|=s897pe?dL~kRh=S zPr=vjk{+hXZyW#bJS4kGuT1i1(UEHFwODYtk`Y=Yg@%d*=1OfFRK7-EVFWt6(&pbP z_N8G0K7Q3_rm9ZlE7NCM2%0C-+j6DJhD?ido)*b!mClnt`%V4}Jc#0jRGdi4vN@b` z0aj8R6!}1O0#lYUGy1U#R1s9s7`n>SA=!t_tER5&bsMSf1b&u*%J=+@?3)?b&UOtg zpdt(7qm`y{5)~{ZOsze1P%u%p_;&LoTXSeekS?~?-!698zg=v@0Ir@Y?ti-202M&zvlGG4 zcoCdMvVr^miH4M<<04zD%9={d$2ZMnAWz^&D%|NO)p=|vA8E$e?X+Ht#L{UyW6|RZ@i?z27tKw}Kb?KIFL2A)RcQ-7g8$>#!O9Z65yBnlST2bk4kW{+6 zK^i=>z~B4Dx%S@Qe$V;W%WIf5>zU`7S#!^QXQ-yCc=LtQt0W~(AJD`8iCTXqZh?4# zYo+-LPSbJ!yva@52qn?fPZMI2&^%mRdXXn$akn|xZ2@>gEDPtWal^xCetQb%7r7^( zC?+kL*kx1_sw1%vVsw!Q8JY*_K^16=({FH~1QtSlggk-x*jLA6KVv7{H0OQk42wyv zB#A|lha2}r8D$0L?3;d$%32d!5N%S{u)jw2@-sL!1dF!9d1oW9A+GH0Z^kL-V@XapNlnL&8(CoO}+{|V$UfsIN?-P zQ-OezPqbkQMo#NH`*q@L7bN7K093*_QiulfQwC$4wEP$XF<6W$`M!jgxHL@(?H_(} z{rB!b#3#YMd6Ij6MB*KTf25LjpG1i*b6dIjAz^IbZ=rbZJcY4)0Jwno*AE|&yVGwI%74l` zCL0R{a6RetvA(5%$`MCe9uk#!PBfCzk+z~GY=q zPW|p@5sCXbV1Mcz0yd?Ve6##>lVPD8wtc`9wGbZsgA=0o=-@I(te9F-21 zK8~8~A2g2$c(j_}aaj8LdqA`JcGt=p5fh}O7OHhr@5A&gaHtDY zobJa@*CWl&CPxru`?9)K^V>Pxajj?zxD0pJINkP0D#lZ2!mf18vMWOkRUTcuND5KP zX*FuCXdg!X%jxK&Pd^?69+i?yRD=@7$3Jq=pnp$&{+Ch`A=lI`xNrtVdDS<5H7#&b z0=qgADh9%|zWBOI@cj87{d>2wJt5hw{}Tzxy!ggz!R>5K9_Iwc4bER`{t`*BmRxW= zm;c$ZtnCXn`YOQ}etm#}^2Er7C2VuyqI)nw=zR5jkNda}ZJ!F6QfBC=3eYH-g;jIP zvuCh>K(h{S)_?-2KAJ?DAa8-0DfTbUr+24CbIna|k~mY`>1g>91!9sKT^rUO`MyJ{i&6VjKzgZ5uP?$j#!g7cEWk8sCk}W0H+{Nj^zG4H8cv= zGvimmvJ#cpj!<;T2a>wT;{b4I!~|GifB>~KY~n$cfq;m}{EoU(@0B$r+d(pgvcou3{6fK39bOR_#(4vm}hu!6XsJZU*3n+k$ z{H+e?&|Wdy8LS>gaGYK~gBm~^*ek5g4U7)}kNj$%8Wh95ENkKe9-z{h`awy}uzcom zI-!F~1b}jGZnLyBIau7G&br5Hx3@2)engJJ2oHa`&qeO4e$gs5{C79PJZ-F{13+2# zj1CpoUp#|YIWX9`4PXF_Uqz&FwpW<9pdTY2hXMO>y*|NJ^OWc~XI2vc<=k~a*rQc)&XZj^M@C_vHh|+FtK{lGR!Ly4%Rcs*v#%2IYHU$E zVp|4hKZdc}e}u5}a+USgKZdcNFvciadKlBX$1rx}DspX5Hw8n^xs7xoh|k@m!`HxERU3-Apj!~#@?1dh+&Q1fTKg>V2-;4;6)C^ z#Oiuiu&*kXoP?;+@-op;7c8Cm*OF7HpuTE+3g8PHxP#g_m_~12c%yTFeS3@!MLqhc zm0aEq2q_N!ktg(5D;e`RpmGDf&Zr?;Nx_)^&`PQ{{nbhW=!GEPMir0#^4?y#5{nYF z(&@Y7RPl9yO1nsCI&%dbMA%NQB;0-muGy#vlq9zn=U>0>U;m-X$yq=Sr%Ja5*s(NH z>}B(~7@;9J7lidyZ}~)O_h!H9&|u+$8BpW_VQijYfUQ*R2g2ANAz|#rR*2;e8~B|j zLLFgVj4A3TVX5*nHo%N_MX5L#0~Rw>19HQ_zNzxvIe4F*V&ZOw*X|jOSeI=okz}sS^${|3r>ri@AnRN ze$3?93oHS_%dOj1OSTS(bcr)Qpa8I=`1&cdnM-+*(D00 z)b>|ur5kwVR#r;UV_{<1MP&INC=FWpM7MYhhzhPu|2-PAsKsqODafLTKBCxSj@!E&(El^lmPEP`xJGc|sHk~Dp)?@M@Ah)0Zd<6`D_ z&f)IiM)1QGA}h7pYq^?VeT2yVsbBQ#07xuVToq}=9GWhj8yfzRB1^ZQFx1M9W~`6+V={TJzVDTr#Nr6n6cc^DuW zw<*(}(&4pTGpx9`V@5j{1)_c6WKB##0-cFYl?a;0mM$D_1jXi-!8C{`_&A0bqI9~)rI39s=(<90fSZD zJc4CD01M(%1zyaF8@P@TI0>P2x=6}_EnQC=SQ`x@A{k0o2Th00uonW?#u@6`;@6+$ zWxL0ZL*r5ED&R#KE*$X468%+vIv^Fm6tIh^y1SomUff;W{cc_I0eo1e=hulq8+&Ef zi}HWyhO8aenY%eVyZJq6;A;JE;*cH$=l6p=ET2Yxx1t&*2qvk>vz0((f??|T{apSz8U`8U*%U+0fr zd_35jS`5B|c^6-LP@oOQ^$XSPEB|%$_8adz?EVD`9ntVy^J;4})q-nhm;zq^S1*TC zf!DvpchN;514C@!^|!?Zz7_#JkFWQzKA}xbh{dl!D7cZ{hMtagF zpJ} zJnckB_sQjD4ztddQVjI}2N1INcs3v*XX^`@qQMc#BJ{tfhYV)8bu|!iUCJSwxAB1n zXi7wN-m0PR{f7Q|7`I;eqVhG(hq8CtVOr|`}VuMZAJ69Q&yULRXk-PU@1 zt!W<4dl+TX-smsDX2{Geu{%YhE7X+g7_!FV24Au1B8A7~)1Y!+<*TEqzTj zu^B4Jj+UQ2xNwg=S5c**jvzs!jjstlp;M)EI0a6uvshe%-4Jr5EFjVF$ei9x-nV?X zzh4+xz#~+2`gcIcPv6MklLUNNCKvIl<%<5VK}cYGh1m(^?|;^a;WVw;+*30-l!!8 zl3kA8Gkj0VTYr1HXx{IL3yUL)TeWM4MRsisYGsbF2u%=nY=OCUuGex#-QFa76Q0a0oQw_k}x@Uy6Fj;KfQU zzA0SpS^UCZzfu@ynFf%yj!k(N_f}XO2)+4vU(H zN;CrigY4{`r2#NV!+?sw(kDS=l_t}U8yV@F7de?^Lf2cL-a~KxT-_C(-s$(V@8NDl zE(+~3hk13MQ6l`}_QJQ_{P+1a3t@uhru}_J>E`T@O&8bi-->CMB7`u~=es9D#8D&T zYG-P{xvHpKb&LHba+~DyI=$_!S*59VI|Q|iU(iN+r2W<{rty=;na!b73Ty-(I}o%T z*xr8j)n^f`QT!nAkVlfGe^0*1ZE_pgsmu*vko8=R(`Nq(gM0^IkeVVPod~>+og^y= zUj(}=%8F`lUFVJvHhp@{@(4*u<`9zN#T&nvZEZ(KHq=bzm;`gWq`gBtQ2c@@$+6)< z@V}Nsu0^s2r7Dk$-A5fBo!^;yyd5p^N$6aueV6M$Q!0%4Z7M%2JF~G5=#gj~Ri!vq zHNBd;_fUxejeHN8D&Ndv&0G9vA2cWXdgT5#GIT>wY+ZuBQ|8K`#YaK!i>HU#(&=Zu zdOMwB{dc2P7@ui`&FBlDN4e>)Ji=OB_Qjm$*Jzw0RZZZYb#?-jj*>$L1OW4}| zqEY>s_g4^^`BxBm2aZ#Y1B?7i5Sjfbh(w%mF5O4wUWs?i+{mbx7!5mP&!jgt=(Fo5 zWIH?jW3m3V#Dz-YkAFTrn|1u3m=#2atWKMTA!HA)%GboH({GwDNpCgx*l-%H$*GdE z<6q`MrN2Y~6?VwCoTpwJaLodb2)36pM$8&63_xoG>#WL^-E4%IeKk!(VF;>6zl=VW z)Xh*oqL7SKg5P8_nLZ$Fh{e~87E!XYlk6JoO<4uUy+1ogDy5G5JUZ|02BsEBa=c zI1W79o@ABWVfftPjVQ`*WTPt%qrGIzSEeP_DAJ)+{tiEjHiMBOae5@HM>jM0ekk!6 z1+TPTu+wu<8e@^HqO!)Fk`Mn}*CQUO=X=?ghKp5KV*6~&#%|aY_iIxt`sYsd*{jsH zkNsas88KlM zgW22ag{ahj!$&)!RTNhfXm|;`BSr?=PQQ8TWWQA>0H>j{rkv&uZQtn;57pP^n56Eo zo^{hrfc=s8(_nDE-z5{U5jny+eV|WBAMoO$Q=<^=8aiUPn)b5oB7EJ(^|HYu;-T zGRtFmOhXKQFi-04<-lKXJ6+0hFoETq(w)fTG3zos1^z z*{sS0AYlCKhHNzZPj1NZzi!BNmE^*4(dSG=J9&P+(sV_U1>}ZcLogs1Gy01{_Pqj7 zNXO7akSMsL;Zqe;U>fW>jJ}n9M>wDa7afutIj3Vgsz8x-;Vrm^_<8a0*wxjkWm_{dLbQ1Pfx@tDj+FyM4F5MQxTvJNFa9VC-I5$P-C^h*qz zDSkp5RP#=oAfI3S9XCF>jKtF1^0ATqauCTe==H$&FlL~Rz2Ur0(*+LZjW0O2ov037 zz^)N}j^8i&QH=0xw@MH0S1$0Cy#OK8^jjwSS7*}%Wz?ko1W}7kMw_4Kfn2HMZ?opC z!g1|3kth{{<1-n}McSHHK~xbm)$1_Tpf0d99{mInP0}W=Sp7PnSqsH379a2~)b~eb zrG}WnsEy=8x*5@u5?{%5ymGql=a=uN%SSh{MA-os$)5RwgR7~UY2WZk|1TNHbKOHujxC|D@RKk>;=hg|2T_AkQ9hWt%)YLAdIg(8jPashR%-1AkYvMe zE}tP3z!i|RXg@nh(9Wlyq_Hs0DM&TZ6UqPX=N0c1n1QNrfm%Myih-!sqhj5$0Z~EL zdRBNE1u2)Abpj&Bz0QqpXSN8+NG82RGZYfc7R=wb!~VY*XXghtk;6@QXL*^%O)ASs z!|oRK$jhNz;*@o+m<9q~ns7Mle%mL=k(W=MeBh(SN;rdSa7>>zEI?N{*2Jvqcrmow ztHCWg6rP_UqhZXcb`6f?bc72Yp`8+_^@q`ChSJwnNqQBP@+5Wmb!>KX6A>+21ZH=z zR355i;wS`V#&b$}S6(*=43DIkJDU0%!_tQr40dcOLzI*$X+N25Q}#jHJ@Pj@*38D~ z5gXVzU~_D3of9jQlF8`xi$Ga0Db4IxOfx|K2|>r|SLstQ=t6MQMp)B6jIvBA=r|bj zR0Lh?p`s2IU&Z+~ zVG2%|Kfxn!eVv*8qgwih6-1QMGlmzI8o^%3?l-*6ISj|W72CR?jDs{m&)~#U1c1L5 z=sU6&!e0|Pz||BJB``*UYC7bRglh_FjYJ>mR7l)CrQl)SK*0o+{5LP;cIkK??Q|}% z{1soLKfIeE0oQ}%KxJHH=f24czF#q492K7xWS?igRs>8Z6uMHv%Jw*o=@eV`=8GVN-KbwzJS+*T7rG%J}Xf0FPdyOP7 zouZiAe`md9 z2X&a$`{~8Fsf&~!RDRl#y0~)iO`MZ-6iDd372Xy3A%)}X1nrCM|B_0yKOVZ!P!QXt zK~i)KK`Gd%MXXXM3N#%NyYXgsA3AdgiPknkHx%m@g@)WsT4-gEa>0+KTIB*#Zvt*bxmBn2&nB6YndBiZQ;6}pR5C6$ zt+w(1Gd1Mw&qBDxu|bP59!w`9BjxN! zF+L5+R5F)A>rAUojTiJEb^hRxT1T@=IbrSkr||oo`o3~V2CzYM!!UTKE}PE@y`3fE z(e|ZqhV;~`!yN8(#>`Q_O$~K^$COYRc?nMs6ThO3-GGUu4m2cIqBU0`)(cxdbgD3x z6veb1Jue_?XK;~n5YN%a$PDx-soZAdjFX<~rTkiZ=bngY)U#<$6KP3XEzfQasxBBo zogRDNug)FXkCzn|T`lIoQvqP?6&eEK^+abR50JUsQO0GJ?0fCfEg3kM(@21 z+GtN1M<)4Vg^@3 zjkDL%)@5;HMA;VWcTS8Dibyaac082(?#lsR=zw+vn_zXJrF&Zp-ydVU!Jl8VTOTFt z%~s-D>AbxE@a)g;Q?|i4d~rIvh8v&u)_YERP7#~dta}ozFCz*gA@VBdHn4X_poEggXXBhT2%Xv z11n85ZLSW(p+ZSNdp1+jkJ z43N3FoRyxOZ4Q0Ng_=Bp9PsIXYk)(aH zNIoo9$C?V$c&z6bbd~uRmdBy`&`08q>A%i`6;%&U%05ka&@hx1HZQ1%d&q9uo#xSkbhwLpsf$zoj1Y{r{1EZHf6)ksMt#Tz5R;)4 zR9B7H@1}`)y~srR-6BE$Mq-WJK|QCTrtd;Nqd|#+Hs3kge17#zcBo#4H6AK5@+gxn zvfi#=K`q<%@+9l7ehqh~lv*uaVi91>LQl^As-M z+4rw<<7jZ?p?R^BWJpm<=#xYjZsbCV*9?56vFBKUx20L`gqE$zF0+!=v9&p(sJ*+# z&$5Lo=t8D{uMicM6^73{WgUo{$A*(U67gJB+IB5Cv5Zivr;W}($qcvs<#$xpaIQD} z*P=K{22Tdb_mNI5b@z>KUME{u;Z}w~Bmkn23?ayWR7k=TnN4y#Nz>nf;;|)v# zpOLRf9Vl=k{J#s_R!572_|e&+$aTGeNo(AXnTJHr_c^)6)2TR%Zg_zcG2*+ zAAYA)O?V)LV$*&su$IQkwA^&nxw@DDOsW+lBc=rh-l{COHY0%h;wZ!WaOAKYQ42AdSy^^}6Y)epS92?@ty8H+!EXY7 zm^ntbE&c^luDwFabnWCP=_>~R7AVTjzTLRmzuQuBX>j|x^c_K$D7ZrN6=QDjpAVB& z#NETxh2rM4`bp54^WNBwj{-@@v;J-LM}Z{oXaXRRgntr9uK!lkP@uy2*f6T~yp<=K zrv)Yoty9fUx`GWRY6v=NIo`z`ffELc7ANZpbRSEQH*XuAnN*5Kpk<ri-J^3E;h0; z`2qiruq`+xVK)umJ3)s~wx|ZjzAt2|hc6C2|J=d)X%lNp37Wu_%Mz8#<}Wl6@mlWk z1Q~)jIv7n*h~|yRYJ4fC-%(ew!4zSN#sVMxnb@q>o`KfQA8$_l89QjP)yE7s;!K%e zZ0VCbvd4}q^0^EQHkxAL^jtcTX$i?F68D)QOoKm>jOIElYiI+x`e}Pv!P|NULJ42V zisG_#$xokr7Uvi8Ds9CS{Uk-VhlHeAg9DdMstKJYwf3U1u&@~m3zOW^VX5;4n3^?x zu@MtNqo4x9cwaL(H^_JyrV`QmbKdQ=w-qBJ<|iY%3v6LI?s_>68@X9Y5C*v!CrcFN z*)h^_-#X+!FYGlO40AD|d%SjSqrzlbeL;2Hmw7B3whQX@^Qx=aTqS49Ham1~eT}2l;!lHP+ypCtjb0he9I_hSSgkNggiM@s&cN0tKe$m{Y)V)DK3_`KWQt&8n_Z&Z9Q6rE#u3TS`T zD9~8S6Y}w%D{s=l1toLkB0cN&rR6{APNre8^w(+kBwfZ?#@Q<^+f1lxReHEHpC-{iBKw04Lue2WVa*c$w^evK084!%}@&7Ju8X4H+2hn!vSDm6u0S3 zO<}=MxPWww*t#=eYIOVDK+SfkYRP|9x8RYOT$$Qp=Y=opoqQxFQ33H|Mo$#223()C zAFXWK7-1KVDQX0C_Rk#_O@#MWZvK3!Kp-4gW1^Ip?d-LVsy>bOU(6aTC-B0WPCUsY zpSA(WBSo&+DMZ_4GyJeAMDqw$BZ1FXngb#ukHCXqsleF?Jpj9~Ug@{p6t4m3*KwQeAU zm#u?M?9RVmJDA$o=swF3f=UGTW%$&lvwZYLHjH%04HKQenpF>43mQPn+KKjG4i|UP z%BJoKcF}5U@4jfGeGesficLp+40qfGOy$R>XLgRrKGvT{4z;GCl~x~lNnl$S35@_R zNg8U4yOL1+*%4Nbo+jI)eMi0R|YLe?tTjoq>zB457?y*}EZCQssR7I{GNMAaNlZIqQF52t1}RVCty z%2X@s#MR^}LemqGOaHn;fyGleq)gw2>+!8ZM9nQBKJ>78<)}DvnUtd z{(*iaS0J48?jLbv%8EdG&olaK?qggE_>@O+B;sFjBs14P;z-VE0!fdD=0CS5a}9?E zu25=}4TYfp6w>aAqAx&c|3wESHBsyPG3{CcCbv=Xa^1v&3v>-=P$#sX=TvywRUN(1 zXZG5O&{$58{e7J97}5^X6UtAHn*ifY!L8-u7$w$!)m&JB>$i zq=4YB!;;tUZzNthqFdF~lnrmXyT2XMe<5F3^f5NYVjdWEd1t1=Y-{=wz8!h!tn%B#?cM=3KN1Oyhvm#*cpA4n|4W3D&&n{LqP;PohfL!I1?e! zv|)}AU(a&qafAXFg(A$a3QtpIEQEqQ0@Ez;nCA158b8ngXY~+`K_em+IUYG4FwIaU z)jeEK=j+m#?(NgY%4L=cKIo;cbeDjojHBEr)CSb%8o@ytbW8ZirP{tX#@*1eZSEcQ@Y`?Hfj+)mPs=6gUd)zO>DX9id<4W6bIwI@PZKGu2AJJnoShp0zsWM4Oj9Wh__y0tVYh}K-#t#Pu3=ntCPD(`$k zFAhR`Env0foDQZH(cDG%CPCX2X*YL_ZiKu6 zoAt9LYkf@-chKjtpq=&ux%urMk1OXd6h1i;^WXyL+MS*(LrJ_<`0`V<44AB;V8x2~ zXrmeQz;4C&tEJ4A$a96#v}lw{zKW28p~%g@t#zy7derhVVB4$WbAl_MU{DL z{?6SPnA|l)5}MvWz5Nq~Y~1E3a_lM+a|5S|Y5*>BsQ?r`u=5D?88Y|f&E-Ngi zMF+$qO9hZ6P>RBD)e1-z)`D@Fw$RmZ8Hi{i;-KV#b1a~42k|3a_Mb;(B*z-=`NmxT zNcZoD(=c8*#V`mZsg?6aXcF)jJyDXtR3SY=eh;f5?b2Is%>{FwplRvhHgePXh*TmqKW7+ZY`Az3VSa z$n+!AHA*Pu&&0n9jYh4RA<^*S5VqUxf1^tDG3G3I+gqRzvLT;%b3A$?ysk??*XLM=!c4`H0ayNW%1pdMoXZrX^lmjk*E&Al z1TXRE5Ka?XGmW%d(8eY3DvIprLkV&P_FrG*wpG`YFY?UB!yWKN_E{D}AW4KrND|`- zl2m-R%C~EF^B0noH?ghn=z4!NdT}_uGcv-Hn zo24CyF0$ZJ7n!{e=pxy@{^}xu8MXXwze0a0HbkLiwi!_~YyveA0!i*mG%0%?IIlHY zQY(kQlzN0DWg(CxFokUVDEJ4GgzUuWkqy9EuC59#ER2aVI^Ot=4+8}=hJI?sfX9;2uL{L|mjQ7CE4abH-fgG0vO|~hY2?@O9oF`qRfV$4tiFcP<(;8bx6fo*x zlgNqx=pqdufpR6-=6F8v`)t zdKfBu52ns;BbrBJ7Bmc#=&J|5gQ=?=oTOY5ibd&z7_g=FBdX;)Vou6J310l*It!J= zrjL?m(L;}kJW^iX1Wp8kxOo0(xcK87OH<(YC*fSA+&)|h3xe+<2K}owEw{DMbl87& zkw2!ad`)$+8Ei10D9PyODrNF=_-urVAQS2}l>+7Ebzpm26yTOg3^7c8)Ouscp&p;g zns&e3Us_pxcsV9la6;ElLv3*-MqQT((tkWen#iWYpgsowBwXdVbaB;FNm}7IGv_)% z0h}+ZAy;uV1B;`T3kJ8Y&1?A*u^tgzMT>MYRSThd~pEfc;qr{~I;eg>M zf3Tm%zUSgPtZFr6@Uj6{Hkw#k>DgYkUKab>(B)nI4DK~`Y)=vGR$^>MUzY;SxZKMA^dVaMKDx33%DBqBHA3Vab*5~-ta96A5cO| zC&-GydDxY67lhXAPIe()|U#H4Y!(CRA0o8Ml39J zA8r)3@V7j8@%m_E-+vq0EVsxX?g&G5#kadnS%V!GBaHekUB19;^Bo#weQZ;>4E)TU zrrGrlnorr#m}BT5$ug>zD+qk?l+WJwwI}8Cv#%}^B6pkI!Qk;^?_ImAe{dPuLToeH>U63P(3PS6s3%5T54K`2dP%Eo74U za8iToL4>rSj{U}F@Z0XZUd!#)W+*))OZ}CJi@?jJ@Rw{zMRblNVN!^3UP9<-Fr5>- z_j?A_elK=25ECjJ1_Qots&E&%#%y@yyZuprE5?d#Zl!XfVoK;eH_!sh@_gYkTFUem zd#hU-d(jdRwWoRb!?0Ram^5R0a~b^mYrSv*;JE_U{7&2v(%Ua*1e(QO`#A$A2fr{@ zZ8;oAc$v)B@KdNv0<0$-?ny=p<6O>C?Mecj*xBp^F|uBq`kaiMii zhr{BYM~^1_q`Til8B9@dcIP;9_*3`!?7hZCR411!T>P^+mzZElwPR1Zh6{m_n3R0G zC83Z5#kH$k$F0|_sBWy6S0Xgj#lN)hYrcd#b?z;`ig3pk<&hRkafDZe~^_fxf1+4?O88+ZSvft zW}7VD2h!RzhT9qUz^zo4?A&0A9ahQaKMof}{AC`iyF8G3cEo~OMj=y9Hjm&GA;ZH`(p>{TC>c%=McPEygxF*sAn{y9+3 zrkMx(DaXy&eMAA;*(^=GAJBU+_=xmaV$<61TZ3hK98s`&w~H63T9LysIXND5;$_0IIk(<&#C81W}-SeKvsg17Sx=6!ue@Ys)G zza)q&GKUzRCh_!6O68Xw@;Wc#y0!$Shs`?gYIlZ`MP>WX#VDO3#htY`qkRo|U zFksvw22bv}hr;Jq($(zN&1vK^c328{l{xBpMbO?RyM$ODhW3>#W~TW?y%Ws{KrGap zwbJqfC(IF%h>dmiRb0NNev1;cPdnTzPssp0!9_>OV$I$*Su-_+gqA8wh?;$5>K@Qex5t6s)0Jw)Fr^3grl>V}HFs$F25#t`35^u` z4T=g)(KrFq9diK064|mL1y~}DGW7=v&}T$L+3`Bm(7v1M@xCymnI1aEM@i*FOhtA~ zaZjaZ1bW%EZ|beMk$&auuK8b4vYs9EQGbRQB8Bq7@n&V^wAc_sWQ*noV2CvL8G+ML zb$fYm-?g(46c9P~Ln&p9}-GZV8p1)(z!Y{9H zx12lHe#CUi%o4+OYy-FT-#rK;0om;2hy+RgKGx6#`D^*zqYiLk=^MeGR|(=}*hOGz zn=$lsnjqKD0**GjSMnsLWm<216JUwF<~{R!n~R=|cCOrELWQe# z7TcGwy9=Eh8A@%-})VIR`3kTI(n(Bas8YpyJ8U zL1KkY=bA~dfTNjQwUqQyePwk-_>lB^OgMhB&xrVUv5x?8h-|zP*Zpe~d7G+Rd0F;G z3mDlEXj$<_(-*Si1ETR~yDnpDW`2PR@%|jcNZKF~ep!F1)0coKaw&H4B`N!|hBm0w zsUS_`F`bPzfCQwoKl4X`v>J<=GM1B(N%#;+0HLEh3AhX(B|X!+FuGt|v_U%F7}Vn4 zwLTkyeF%l$KCF_1S;5iz@U_W>rVVkZtPTLqNMFxI z#8tmQ6B>G&bl>vlhS-LuF99NQ1r|N7z~L>j1{Oz{oW^fvB95?uPLK;8RTU(kt*>^W z;4lgon+Xe`P5rrEwfyPpl(*dFP3DSavdR%&?8@VfjhDd=etcI=(DcysGAx?lThR3O zGphu(8YR^sHsNP|D&RNULo-*?CbV5I;xGylT*@TV>dhgJ-rKl2leo1Q^{%9VkLa5Y@qq(fS}G(q zM7nAAg|C=GvHfJDYD4>NjNOM*Rao z!}v(yVr&C230`Nw^xzJH7CUG!yb{eOqbGpRIXkmeA~v(YhB zo(|870uy23sYFu2OKQQBr^_T z?m7LKkji^g#oYN^ATiDyMMaV+2IVoobUW~CR4>eee{+-Iz5gz zDI%pnfT=Dq4K(Z4sFQ25@wrDmMUiBgP&+#AC!vHh)_H2a}umt6wPZ=Ks8%cN@3jaaOHojC^M*o_lAq^-Pc)t`5V4O5OG<#F`3* zf^%dsMpU!yxuWCC>sy65kcef=m3e$``0!GG9 zEVI|Bw&h~{Pd1h#wyDh>NEdb0pU659(` z;opZD!;1%~4rRNK4#~?pu`)5cjnM($?`COhFq=Mx2+8^bB%)FnEP9Odk`AWkaUAyC(LZj09}0acrB zz~d23DW`gPD!tr|$4jHH0m|Ck7)A-D%sIS<*j?1UM;hQ%ToOK9^gTbYT&M$_8sTgDWo4=$|WukRB-WGckJci4kV&xB2{r=%AlQ9uw}ox;=TO6~WJ?MGbo z=dWgug@5B_Z;EDJG_(lC2+bG;EFOST@k=1UQZhZUl$v><(&`2N?Ok)HGB95tSVr`> zyf1r`C5&+lwZH2$x+JSiXu^8tKZLOI_jzkbsxV+Cg(#WM`lY97IAffD;@f>y@oCN z@}e)iwwW&lHzUdUNg^qXBoZI5v#4#jQi7u$BZO@&X6YCJ>d~uF6VOqoucj+gxwt-N(e;I^4rew8-g4w*@F)ZN@D!E130F z>HFG=mef#X5W?LF9)P&E@>v2D9X8$g_EA0rqD-;ROzhg|tL)_s;}t{SE5iUaNJkG$ zdC!4dwT%Mo>~?eacdhIPBR8xV5(wvc{Xi46{VbR)MAZtKDN_oKn@L(7U_6v~#XU_V zsvc1)q*V6S)|ie&>WOcquXu{HW5Ehz>VY(TH3wNMrgkzq{ks4|SpqPWivUBp1puVR z?7s1}{Bs~|StH;66CwMtFz?Ge3p)$@MUuh}Y!&d9*pT29JT9W*Cd&XuKte@9SUh`3 zQWbvYI-yElQNl=6+WP|YkkojUFjAtKBo*p4c`TWg0wS~3z%M|5nh)qt=c2`$xja&o zjvLwbA6cIz@nNMR+xSF{hGViCrPGDW!_T>r?Wqnxl@ajc!A`k`+ogxIN%L}le}&Dn zWuegxox75B<48WK&+exh^^-zET`9_*hR&kfY;-V{E68cv5doIDCJ}w+g9}zlsx`G$ z*_^ha2s}a-EJ9H1AXLEVz~8j?rDK$uOXGmMlxh+o1fv{p3kNxJKt7zFf1T%@c@pd^ zCS@iUBKmw3zLr7;s8{rH3_1N0Sk9IeS_sKHe?q48gmWb@JoXA)ZDYirjZT# z5Dk%`)1VUyOE}3B9IAPvj!LP?Ig}-u+|=VH`EQS9$lqT7P)J@zC2Q58R!9!upLJXY z^}^AId~m16N}CTVTM3nqD&9e}$mBQ(4Np^%GBQlF6Gf{&$u}{B9k)|;^?Mvyyw(t=YVw0|v z|8VGTk@XUX_RrpTeB7tWY(Z3U$#fjnNI8=-x(8V{dK(-b1Ixb)?>uOEu?U_I;IY+uDEw!Y{ zv1V6CoK*w(taHMbagQW@How?WUX`q}_w72r=u30lg0dueokU>twPww=JV%{^FL{oA zjp*S+3h`DlnzgyjQaY#mwvFp25+LaLmR(zv@SN{KkbtqXL&@rBV$3$l=t_Y2p`+l- zh)LDhpxDD|j#5Rn2L}^7u>VY`2m_C$k3HzUVCmt;n_hn_ z{%SGO)>0fKR5KfnLw`RkfO)~wZ`E}9M zC7A9ULloFuwTx7z0Ll7F`ba)-!I1BLS$?>_j=-Ee$bgCI-m9$;Bc`dPVz^_Y?n8Ug zFGz1y&{co3RcyT!4^nJ=Wy7Y#fju}5DQc^;Zv9i#&bB-QirV9U7q#iH);-?ddkMK0 zHT^z(K}sbrnX@9|<-+n_5{rQC9hFQI89*}HNEQSBNL0Wdsr9!%a;hK>@JC)J(D*3% zD4F)rdMnH{{=PpjzHj8xZJ;y&S1H+vgmDEp0#_1W4xQCJ zMmpz46AV-BWCcOx&&Ny-r~tUj4cG_BI97GVyZd?nur-tL=JXOtmCsK$#HGoYPYnxs zoDQK=r7c7;+2^l`AX1^v1;2nTnEzUjSwI^hxA{yl%v!o)hn7Q9>6#rq944H#i*|vb z(kVy%`J2Z*PgwELKTYjsynktG*CSJrLMstLn%dfbn%XdsruHz<)aC}7+A@DPwIgcC z1}%(&kBGgG(_el|ht2H{rQJ(af-_6K&QJq*APQ+U*WR5R-dld7`1>xoTXmbv(=~J< zQd6f~>?pjnl$H#lobfxi06|(o+A50pg7b{Lj1iLeDRIw8VD}Z1rC` zMRDlAhtBIBpfzpWJ=vccr8KI|HIOPMO$PhN7xmhypbWj5(^RG^aB?3U_oi?S9bd?K z9hh1J@>L_Ed9Cn5JsE~dD;u(pn3quMegE^#qDDqrON3Z<0=EU#@{>cd$3tMV&@K+; zD}7rZKc^+jXH4Tn5+os%j$xDrD9MTdSlFn{)y zDr;-boUaeJb_`t)NX7@5jcc^Ep*im6^1NnfZJ6kASouy%P}jCD<3ePR^&Rp*r74Iz zYkKhsg8E!DJ=8JiXd_{yfB(fcF68Ooj6`f_d}%sf^%W0VFBYVj#q$;4@e%(p(8v!< z;2RPbD(?~NH83S9B&G!+73TnkE5gb;+P3Lak=1rHbZyK!2{SpL4}dr7_{;p!NEZ8v zGu|rMvnw-a0fu$+Kk#J$*SFbOrnEO+OYFYl2;P5dBoUHGBjU&P30FJXRoEs%72pLv z5_CY=%Q#?zFoJPvsx;60&aHrqZ15ui#3K1k0*zfc3u2KZg;*pregGCpNx&kB`^O^b zdatTB)jzVKe7nzZ(|UDmg8{sN?$_ekF|6$NPmd7{yRfLF2HIl|(XDP?LQ5R(RRZ4* zB41kdzbVY2Z&^(Y41_H|XLPe;xY>M0e%iQPXeA50j{z}7YhaAN$Ky(i2YC=fOO2qi zj?Qr4T>D8=hWJ(=h|*+Y^Ixy2L7$zslL32Cy=1B{k4}y?^a;1rp@MzXfR|Ao7GDv@ z4*BUHSLg*KwOJ{eSqq~(h%d2#q_&*K*`K6#N_y|jmYBBp_R-n)-ts9ZS(uz7(B(q@ ze@a9)%8|ityNH*kAYy;U8Ro7eC-D&~gU^Y@P^S{hQ|rKkp)`E0HbYtI;NCD>Z#6y= zr$;CW7cdG8v__Ih0tlbTBs^=5@UX}#l(n@(i+B`S8@nbg;c4vzrOsr9!^n^|#eF37 z-l)y+Pa5I|A1tnFzP*PZLXymI?M32eCE-^l&$$YfAkA^k#g@#0egXX=#iOLe)%arU zI~`Bcmb-@K-2a-0-23UNuf5Q$Dg1i~{-hs#NP#07nY^kGkj#sqcQecbj{i~9j!07+ z1m9RMnJg~Z?W!ENE*vhmW`~v#%85^qP0EU*^~6brSY#Y_o0&_ls*|g+aQnJ|{$l?0 zZ+CCsJRZIDe7m@cAduombkMhhO<#jK$mJn1Rew!suq%(O7($EnmQ|SGeL@JVV1*UU zq;#}Anj(_XwZc=inoUqkON&qcw$;h|`-2yp4{M;$912v=^Z*gCahYl^CZ7T1v!fMF z$@_)?$_^kIZ?w;-Oa#|U%HYMc*6Dz(?9^-5k03vk*;!aYXh8^nFSy`wxLWq|p+(!& z4F}2uW&5I}JbQ}@F)q2vzbeU}_DIdu0fIF46G4YD=~>wxZYV*~VrRHmSq-wX6(G_^qQee9K<4N z@V7-W;b7Q%K?OH$DC+H;#-on{Ia=JNpGiDaRFpkiefM-2Cg!gyi za&GwPjo#p|yqg<_7O?AYrG90bO9lo17o4-Y|IYIKA^)`8$qB&)WEN1S1@d&cXh4zad_X$Sn{;C5;Bso`kxEZ7vu;b_4kVW+@m`bFFp ztZ@Yl@F4@kuYll!$f}Lk<6b%SYiPkxw*Lkb8Fy6h{W>OM&vfqv6xHaHLh@w4D!9mJ zB1hmah2*P04C7a0$D@&|nnc+bFU6!pP=rYXSrN3V+4;mXC6iX58c)|G>p&O!hXL2FS{e^Cx*E+JDF+2TJQrp5&2dZUhHI+$(t!mKgTm<`#9~1Vh>7 zIkgZm#Dz&!C^sN5I6w~rE$!HUTH1R+OS=eYY3nQF|AzZ*?BsFgY-js(H;QkDucs9F z90(L+XKai#KNJ)=-+isM!$ui-tY=IZGMN@R@?vwgSxQOz=v4rf){qtWsO)Rl4+Vjd5a@@TE%!_3f7i5LUg7#kr;lbi`m0nY z20R};!mkM@R34lbx)duERW9q37Di6Qsm>FImbcQ4nO+bT-e)Nfg|I%zxu4r=-9o%L z1s>r!Lsgza+D|0R@KZ<|1qf+#L;xXed^3e5Afz2SoViX632Em7A#LivL)yUY=|X<8 zSJy@L>;9BL7b>$Gc+^^mg8EG$O&$e|{FUV!q72!y2A5trioQ6Hk&yh8?qNiXBwo(V zENBIyVD~eZPEyV1f5vao7Xs|gZ)|mKzg7_c?=q1C-`g$;ux^HSlE!WYY3vaC3kjY5 zu+&0u%Ldu3fV-NIw?djXNS6=uIp0EPQDZ)pRT4o4a|k~)FJz(2yP!$#epR=6^iBTi zg^xUWO$Oz^bhLMafbg+jhE`Mz_zK z`&FgE1apQP;IDWc{d~Y;p`XWOg;p#Xl_*l80u5e}F0qaBnzv6;43o46SiQx%9@Vxv zCPjXK^UDk+8dTKRr#ejl`FA=F`&sL-g=>nF1Tt?I+ihi^X@A+)%_l#gP7t~v7Scpk z2BuRQdf4Lmse)LHj2!eb)s|1N2rAaZS*}|sF(gm|M48*nxeMO zY4P7fB6V~hMZDj<@?OuXYexhZ|7mFddrV|yrQM3{YTfAk3<~Y%Zi0%~q=!5s`Nx4L zZ)6T!_AWqaN*mnDL%fkxf4q^#5O3t(U*5>{-tU_5k2f>p%T(1(;%>nEaq|V$l_{os zhTw*M^Ryx2`T{3iQ6S0CT^$E!5hz4GE=tqNpZe=116=&MuR3J>P?t{>;Hq8ZlXx6W z%sw*6_sVv3@co=9Z9jmr*Y=t#GEMC)p+_bMB??*%T(h+T`#Cqj2W9|RKIN;)UabC7 z4BXyrfi8h686GdM{M~YXRmA_`bompkNTp1p*!elPUa=pH>+K3j!1z@sb$;bPk* zq@Z0$o1J?)OL0HAScj41LvDwVXK;u^im&?xL9&o4rxW26ePvA_cyCw*gZai-{_CQw z>Z~usP~hb=f!~cUVz*@OG2wEm1Q~!^Se#*KB=QHxD~%-1NWoOjS(kX@xLCHk>{u;n z&?}5JDxl^;CsbRTUh2hHksv1f? zl>Oh}A+_FgXZ&nqaK}AE)ud8ZANZ6J!Q-e-dhUO3^24+VSV$(V$60yvjjZ@DRy7J4 zYeJ-vLWqA!BXc3r$Spt`c}IhY35}V8A99$Pw>{?OzNzqNNGM1tkJ+J@)^%do4V;h_ z#={+4hdghSJmcWjKkwJ7EqCWZz&_t93shmQGL{!k?mA?G|4^F&hR`QXRLD5#=jDNdWgZngmmmGSNRor@6M{A#Bh|Q0$16R(9Y*QCV+J3e?Myb3n>BlAXF;ehoWGT zPSVIo8*+YJB2t>e()d-;n2~7PlbE6tSmSX^S{c>n``W-ov&H-V8Rz`!Du1%o7u^?2 z98r}lE$)n$tvaUQ<1Cbsf}MW72r-EmrQg;Jx2Vllj;nmneOY$lcPW~u(4U99rvb7^ zJ=h4-aO^#9K53z)>*a!HYw}SZ5`s+`ZRNZ_jUH7aZO6PYZPP*>df(9!*3}a&5a_t) z9PEBziYQC#JH%+%J3`B&Quh?KZ1>Oo)DwIcq?Yi~gkh4u8h1Gp1j|9 z5>+NtTw4zrhqu`&ueIu??0mO7-?e$Ji!HS}7rD8;Br-Uzj?Xf)O?7VBx_ItEx5W9~ zvG=%&tneJp_c$MkWZm22@pxucZdHeextTaT{O#-)!2jJeJ8`gTniD;=M!X}h=id-l zg4bLwP`W<|B8=Dkraz=BpRQxwmdx z^6LhMR~7TBMzZO@E;4@(Hwff$_0hB)M_TXp&fe}=pYI<8FLQqC6>L>U8(C2G9Q_oE z(E{U(@kB(P6b#-tvNuC_qZ4ZNhpzV%ubG_hOnat=FAc-Oc9&{`eX*W}Sgo=vR3;79 zaJaGg6$~G<_Tk-E#_e9Mu~af9D@ekb_5T>c@Enb!n^WSbWzk}LD`<6;_cGtZdvcN5 z9_16;mb=@cV2cOKx;%<#ShEv$;De4Y*YM4=SCN_iry}ybvksvzU-!?w!0*$&G?L|& zPxlUDGg%COewc`AS4Fz}7)gv9h`?(9sy@iz)4LWYSneY-%>|=O_BmXF$??jg{S0Pn zF>!hAc3lN5+Fz;*&r!0qjQEw+k^vIbXvcfODk~G+-}{)b#p<~2OE?2Vu&>oC^bQ}v z$e$+S?1~pD_g<-G<_yMo6lLL20hD&EdpKZ3sZn_G9q*IpZZ(7N@|T<40;Xdntyb0Z z4^U1fYzX`$xRBI^Bb|mk-h=UsSW1^c{dVa66_L!B{rz^`pSZF{wf&tpPvg>q>|$B< zvVR%zppXtnTCuSB4BRk7o%>D*!Ku!zd~kd=a7LAAW}a&-s%RrhTZo)+UXf0oGqeVh z3t%?0`Ml+~P*{&?ENwH85y)JHWMC*g-{ z$y1s;GR}!o&4XBdZI>64x#PgTTy+{;<>NE-7FFSU-9#&?U)LBro2WrLGX>7>eJ~Fe zv^wJl6a+>NvAO}F+?bg36l$x{4&g9=c9Bo#T^|ZtGS7%Nq;t=*wN_FGXk(pTN+DW- z3Ty7~)qKU`c#@gmQiT`L4+gp8jNZ9FoYTdrn#`bDXZxh=8JQyHGM$H~maukC6KIgw zhaP6^a^M+gXQ9#GLiH^i^*Hc(`Zv%XoG&99wCVznfc%}N&{cFI8#t3t} z!8x*gl3YowboQFp!qK6{)omX1%w{OEhm^Rl-mz|kZ2l6a=JCRYEj*tcZFquEppIlqGX}{iYkjj7hmj`4)JNy^^CN%)hA&iYE*f- zSVk4y*D-69=&GOUi9ArO6}@ z+9T~Em1hk$s)tEeP5s_u)XYwoPocYr5!a3IGuuL$+u$2(a=avnkI_k-5&t{8FV!v} zI2&h;qH;}O95}Pvft?T}X2k!I695rL?g+i71rQfTd@PyoHXAyL@sZW>q|%_*IIp&#%EJefH2b_{bclE!$Y)PU3^2i|?Br0EqEe-l>9w({ zJ*V}y(TFb69{Jjjlbenos__Iwirjl*Ml+_|%LT$F3RC`rL|UA>NJS78DtTYl<=KT$ zHaJm&)Wl;6j11kE&4gn+USkW|qYHvMsm=vs)e&OTr#up`1sS8urIbGu;zNJhl0cLy zmu&4J4=w0w#EZwuovM&j-j-x5?Io8YlGLmU!~CDnkZ4VRXvp)Kw84No<40+cVFbyu zE=V_9;+vXO?6j@ax~UaCq?`Sc3G`Pt+f4+8%KEQ$=E+ZWrqd0H^lonxG9z zf4}&dzrB&aGkHT;Ns+WYoa5tB{+J`Zyh)zY*&d(sb$CH~^hsHao$=wU`^<^z+59}8 z;k5(|VsrM?jYL(sCx^^L64n9AkkzxSIVgF6ADilHI4v?p|E?$L3rTL%Si@Z8`bKDK zXzEoETPTsh8m1IVaH1j(OPnNu0$a$coLQ+t_ z)06_@$V%HPk1t&q0dO_*Gz|1{{w(V-4kxa#$}q~2dS$VfQe_i_)6_+OypjBQO#u*Z zXWP zW7Xz*-~GO?X_XQ~*+Eqa-N|8<-zcR;uSNYbTsKJ-GcOzD`RX07$H+IVxVV5$+9q734-lQEl9S9GbM&no~ICYr}urTiuc^M8p=S8x6p`>lf2 zcsxs3*Y*rS>%3U7K-rL9DIs#L-+<@Xg0~|u_9LC*&T+C}+}_j{T7dEcmvH}n0ClOr ziOPi{b^4TC12bE(X-C)s4}d&6Di8s*#}#LSFN{_JPmCp@AgZ#lGD6v}nO!GrEdhIb zUf1RMcd_T{J5HL2_tlyN&skCmFJg>>pHEtmQ=ebSX7})9i-jbmdo+qSxw4&DX_$Q| zyZMEY5=Lalsg(Y(t?>Q~zzx+pV?{^&pYhEUU?XxPBpOlf^e7q_!yl{i7=UBCMM1-# z%oYrSuU3aXaRwo%SQQpaM+S-2M9$@Roj*hzR|_N5r5Xscc$Lc?!qUu4;VW)P5sr1hZ3%%L*#Ouvw#p)f z{0Vj}p|(P`0{YkvKp%Vk339ZrWI(ku);Sr>h!iGuKWFiq4)U(&{8PwAS9_%O470?N z4x+PvP@}r*iZ<3EP)VXR936@cgX8ocVh7Eq3++2e@sP@{Du%mD!-H$$RR&7PY-3Q8u;d&)!_!(oHIT=UFvQAxjJiqzIkvem8c0(^TOY$b4g=e15hg zv!^^>#w3wuPH%mZg)EEUEN?KtV;~h04^*)~;(!69F%*I{+Q;GyJQzTl*e{0T{~(Ri zlz$_QlxY7Cr19qha-x#sUbY79@2;A>+D$9vYX7%1<6A`Il%mQIpixZ;0yGBv4K%{( z>Yzou8;M^aG@vplm{>*ZkPdd{q*NX?1e(~P6c2eQ&{BavXN5Gl;#A}Y$z&%RDJlKa zsG+urpr`^g5qx6R5aRt#1QbkO7|LNEBYoQfLVplB#Mr51A&W#ags zCxSH-C6Ac=WBak-F@c|1YHzsAmXs_E#5ZDceu4y9J);kWhV#e9bcrE$$eVgN#ZRv* zbFFMgsd~EmPvO1byx4DUN$(`3O-bc?zCCN~hI!W#@tKp(&rd>AFs3@kI+ zO9XQX-q=e(6&r%}8M7M1m3;+sQN@zA`<=e7>6TYFNuVTR5XVcVf@Xlsbef?u->Cqe zQQ9zw8{&n;(WFi>b*hvpN3Y98=Na9n44^cNME$%lPu-P9jk!&PNfjV>6CO9yBSR0* z!@cSLeqAzVRcu|sssx_n4YCipFFC-4qKJK8cfRhGlG2G>N-eQ=@FSX4BawU`p_t-m zb5!1mu)H)aw2)AdACEt@(0T=2TZE7%dW$HUvRekhDsBeS#+D15i+TnxRrHa89n%^% zg4AFTIIMu8k|=vn$h4g91}r0lYN0FsYGbDl|NKm^rQ4nV!J>b(mmYr!pc;V*Q$SP} zK~Y)VEKL-K4wadwbr+3y;#lC<@Xy1MwEs{;-hJT4pFDgdTLT?ru4j-sMtYLq1??Oa4C64i(U_BcKv&V9x+jqRASilwJU!( z{nqcq5-7$3(Gm}846)&Y3O#&H(|d}2NzJT?k5%me*T{otj7*l?I~8gHhSPwqN#ilUF%jYb?L&=j5&-pjLmUXB$oCj?h#7Pslf-**vO3E@Yygm zmABv-LX*RI;hf%A>XNhIv^&0jhxv+lFmuLrw1$GpOZ@rJohNoStLNute}iG424z)( za4BxhGzfVcjt`u%=FgRUpW*py3|_sk(PzU)>cl^4wJesA=W`A*qKv(4v^{N-1}I`+ zA}yDNBeReq-EOp+g4|SmB6+<9d)yv<9F)sa)#H*H!NsRgVkjxOjlX)1P&(E$Rv&g4 z1H;nZ8x)nRqaCpGvIPWW|Fu)P{I~9k8j9!toq<=^)5;q~N zTOtB~s%Mi}S9?>>w7Q5=2d|GTUo~R@i$yC=QPE%w&QLZBZ$h7rCiJva(^4)0&w{f< zR3YkrPBfw;^dw*4^Z8;vg8Uf+9l^JZ*_YW=aJTx-X< zXTwPQrgzKQm-JF*B|n6Q8vHhw^&-NB{YS z^Q@KQ>pAhwQ@!+bV^K9uBC^n`0wdL~#oM~B4AS}6W;zzdG1rNa?#_1_w{3ym@hsG+ z4{MJ#W_FVUl?FNwk1$9R+>IZ;+=$Orp~zRxy<=vfiMX_$B69v=d$Gj6f|rhtXtY7= zhu*d5VSD)K)^x&HCH{)92;=s(f^8m4?W3h0+RC2NLiGPvibrmWX8{?|g%GB(|G&sI z3jM`2lD=N!mo-C~{D)~Qdrdt)Xx_wUR{b;Gqv-!vlE<*a+Yrq<$3`A!MFuHD{bV_WIWpP-Ht3=F+#f7H%31rr&0w@SKe)ToS$6<1rpufd~nGq z^`^z$Q<{ZF$-I?j`~zS{7Q8KTRH=vf^xt+KI|W$BiNujA!CXi$5*WGkG>!KW+@s zz#zwqc*xN{ntVIm+xxI_1U$pwYyE;R1vjJ3Mm~J;QzCYPSe8(pRuB~Iz93637bS%> zsJ}9%SXtM4Zt3ZOQx8{f?4~mrOJnK$v-EhVj-lw^f*ODR2h_;dw48Cd5j0!(R@Vp1 z2kQzCT3nc!)y+E3X%W@B8`2F@R7Vsd>)mg z;5?`B@bc1E640~GS4Ltlg6J^wb<;KxFak(%YC(oNE|9HF>5l8w*dA-@5>sLP8)I(H zPtrln+(a@%LeK$*2@Y~Ark&5e%eDsto8JHc6y51O*WI?-6mI=G6BgomoObWWcqNWT z2mHck%)sT!5C0DAjj6GOh5IVOF$~fAy>-Gh05;Hv$+`Df!Un&+hK2xyeBwNB4<8O~ zIo$#D;|U-P`5!Qi-~UTY<4DWcfh8X;AL9s&~r*@OwPcXa#S zwsbe^a;Sq0A@!(q*}~Rj+1jcGfgOI)V*0UpepRz9v+zpqdW+S;5w;ggWJuk^fG+{d zh^?wFjsl#jAA^Ymk7VK$FsW&wPm=Xb@xs9x8Hq~%zu|dw{e5?kIuOy)_T|mmkI;Cf zVaKucnW7A-UMsPVSY;t*Is`g}h|NN9qJDWrzO?ku%lMo}@iS0Pm54hEM=2FdDYv{^ zCVv?-ymaMX`!Pv%yOp0>-?=tSAZUlisZXNG%g2YU_EH9rMnGemsN&!2fu%v2-e##(W@UY+zq0U;m6 zy$ItsmdpOH5sfc2?C+y~DLt;PZmyzb$8?Y|zdnR;i;*2vk`z?G=6ao(Po{ybWffnA z3~}ahq`#Dl-dAI-r^_Q3r4crW=Ef-hWu$RXT0fomk^6Refd`rZngHOf&GS(sbgIz_ zH6rFeh)OB5exPiEP4kjz#{yJhJkn?qq+vMb#`XZbigokB!|TkUz}daA4V4{ahEU$H z4ETATO-Z~|?u0+v@6bZqG>udC`$j=poFdfQ&{#;WuNVr)672-gl@5M1B+c}KUN5*I z4ma*CL$zgVUH#mPt{1TA(4~OB)&Pgj2*;v2jSH4W?!FK++*sVjM3&5-?~vj-b9d_3 zEe`Q^Fbona4*&~Gs_14s{$6bMs^1RwA+<{tZgwm3x115|S4AZY07ZBj)e-bYP(ULi z6e%BAFBeAp+VfM)5BnoeqnHwD#bC6P&iW}1AeP4FPLvp+LW3aa9O(M8>2zN2-u~B@ zCRv|;ddwMlEdRgOJicwwHv=!fdbB$J0uv=z4txOZ6b_vfR3%eTQQ#e{ zxUQUet=_ogP4M5(Ko7)N{Cl3q&HKKr zB8Eq84fi%L=xkZCwHg>fmXEB#8d}%rRtP8w9oU-aA%(A4Pj4xt zr0xPp2R9uR^fCgn{c0|t6rmIW`y_#4;^o!sJVE2U{&FzVyhZ$6X{TRo5|OOoDEg=# zdTzbO_6RVT3X(a%(KfnNv{ZbO0RF7wMnwhP&~Q9Kwn1jwAT)3(Q$*TN&Z|vrvt!$h zn7jXm=Mg(F>fxT3!qCW}_?ZXDBBV?s$Et&#=>=|*RScHjiP$rSx0ROFJw)fF2Jn)WYESVY{4`sVGZ+GfczB7Z2G@GN5}fa~cX6bW7= z-<$x3A(uZq;%R;u)B^1Z933`+zl)#MJC=5AzWbiRc6l@fX2f$K*$ogsr=glHcLmd% zlQ#q3@Z-ec86+QToPVN}G!FRF}cceV{=93r9F zec2u-Nz7k}c@5x^_moF>#8QZcgkrxTr!jn#2Yax`8@R@1wOL)O!Bl8;NWS1f2-`8U zN2qMxA?*VJvt|KnFTJH@-Hpxl(TV%QF}ZWUH{SO^LW+%xpK_xx z9-kgQ*-#;gqjW$CWlVn`iVljd%0GT_75C=h(B7o2uHqR$3T-0p7W`gIR`IA8hglZ} z*#Lm7h%`QO9VMw)%C*Kj$q7?*B0<2rP48L&mOO`+WC5gnk`1dui_23(d9N#x?W$vL zJSJ=MKfGjTtN8-)6B2OcZ!NZ~1b}sv(e*7%iQA#*YU{em8>VUDvC9jf3L)>Qu=)a| zrQJqmUBY7^onISoTouCtf@865F$KQuEP<+I+=TGi=GxB{+1b1$p}=!{nI4B_L6jta z;M_Q-_Xp=a4>&k|eE$y77-VSU{hY)-gCqlSh7XQERMnbuY)HiD^2~sq11GQIIR%bbtq_HqQAdKv#Wp-!h)+^qkE6Y#7073mLS^p~_0`*aD zr&1=#Helpt+F8Zt0Ql*XJ1CU8ZX*#Naycy_D6qzRw=;U1g>zUyP1~H=`yFa~Jy$}# z?sTPR=Rx7$@jSlq60ph1l6b>u8!6QEQXN*h@~<9tD3PjZ+&?{RfW4JALbN?AXnMeKb42_1h(V_nyPtgxms9$?ucd_X|bT!m@$nW%k{Jrd>9$*X9!F5xolj&&e-Af*TWDl zLN=)AoMw3q{Dmqfu+m-pC4{p580{HD7Xv=1h*k76f&ckg(Fgmj2IWW2m}oatE6A74 z%AT%OpM8N?cY)6mIt{`hh6z1M0V#Ld!Qlzy@TDYCfO(y!KD>&?hW}l)sOi`Vx_rA6 zK)eR)$Kl#wuWdr}12kZ(v>(^~#j@?&mJ4@B7#D~xGKHQQV5E8J6e;U_n_n;nGHz`E zpbe4283#pdCpH@izs~-AxHkg79nsOiCbGYQ)*neP_GHtYoAZVB>0#zEY-Tq;V_GE> z%C4mQ9%NC^pF+aBZsNEYnx=mQ2yG#cLx%c^fw`5v z@1A*0@h_8JXU9oe(+o)GB8gTD>y3CxOkL;12_B(`!YIRW`0I*9laFL!llEZgytnAh zN1y{KJ3}c$Mpb3>I`6fNu8Y$1^EggHK>qn(mpoQ|io35`u=Z-&b>9a;7m4og=R}r{ zeSPs>=ToRky&)XMlMZ+{-Zc;Wj`aigiylreRDtG0?A8I12#$~s*nz;(Kc4O8{7Cbw z;~SL=sQ(i@V=sN$`>YK84BITH)^D(-fZujvB@c4MPCX}#dF0vlB|n6`lMJMC-!Q;2 z#aTnH2&?8;@l|6?3m~>iCn&{4>xuGJgYbK5=9@=jHw}$%n^pq(vm`U#NL7W!hofw` zLy$WyU^q{a^HV$AXC+h5Az+OKRLMeLIHU$DPzIfyVM?J&ha=T)9+~EM?w{}K%aZ}r z-k%Qk#7xLv9qfh6GoXVVGbJ4u=&p)d-C13*@gf^X9xfg)9atSt5B;(T^u) zxKW@O*ylqZrfq)=t*Rs}=(`2LdXw~pH@0q{JI~Hnpwyt$OoQon+ccWqmdZrQ)vhA` zdxpoXPL%I0+?}P1EnN{=|GR?6pF@jdTjh@qp66ps<%3|&?dAMf4eo~A#sQV=cwro5 zif>|?)a!jNYTeJGex$mJl845N7veGnOY}O^emDn~lAnMoo&=24?=kkuihD~EML0!c zH`+<=*VhvT`dr@DHpj4G-T2{t6&gOqjx_|NDTx6ngx!2D{@xjYeKMXtE>;XR2zx+t zzy(YP=5_^^6Rwi6uP#I|14d!KKqk#EhqX6twqvbdi%XZS0i)-pQmmZx=!w6yDVb z9U_O#Z|~64|5~Biv&7pK6oV~|FM54k>pIY>qp52^z4Pme~mya=8<`-~!#o@1sLDVTO>f`I)q7mjgjY1BPj$y3lyTz8&b}Zht zw-WMegk?Yk`^CqbF0?Gk9Om71=JYuADo>B49~?{fx~6T3W+gP4+L%+Hc3d=X>xrB7 z7x=axhF-bX9e>l01chFte?; zyK}buhD#!z&ixbPjpnlaP@SO+svliRbW6^2V$sVBZe}D@h0?2U-W}}9mtPefnHRL1 z`0FiSm>9WTX|8aET`2@U4>ub>+xgF)$2C;qXQL7>MZ}DFSDXhN3N6$GFOkW=`#vwA zJ-R!Z6#Gyl(MplMJgTpmaarL_-&be$CZukpn`g5;KH{xTWH{Pa&xH#I?j-Tq5u?lC zC6}w93DExFwl~L`RS{c+8;^)rSIJ-^kih1%+$nnu8WLq`<+ekmrwWW7J@4abGP1Nb zzk+N-=xz%K|7?^H*`jqhOwhh1-t8Mwsyoi?aw@X(7_UEnnGDaL?0!dros7kVLya!* zilg;NZr9v+oK~QJ!cl8gt-+h-R@mG$YWAn2j&Z|>d0TxK{JP`ilFXG)hK}x2{9}3? z$9@paqk8?9C(WZ6MDs`@{V$rw4toPPKD?Cwta&WzSVYVGeQ=1DNwAK0S1!=BJ})wB zzxW+RvlzlOhQIHlxp(or9XlbLVWu2smp98Q_?u|dYr_*Lu(!94Dej#gh$=9<@z#0d zqx1JTyFHqBv$1NiUFCfAnzybp2X9#}kEhGc?6xNVo^+SH934lzLa14@yH>WgMhSZ3 zqbOr$z5Zx+Je>M{zM;k2TcAfpHDf5B9phLR#2OtlOjabXgn*iiCmWuk{IT^Gpp4&& zlr93DYCU)TpUXgoYWvxzx7qrQqjQt1b?+4-UMy<)Y-nt{pGvUCsCw) zx%l=qGS%!4LPZ2`^H-~e6h*(w@%EA zT^yQKL1HTVH=J^vazDR+&ZbNGI+Q}0sLNIz2xI;g%3OHdBt=U_%t-c&9ehP`s;_uU}GM%IG_0`Wug5SeiEYrOhH6e$Ag%5j2cLQy9tb zVQ4lDC^oXAN>nxz_Tg@%(;w-3w~4PNzRHq&eGNq#_JOXxsikE%?OdB?9AKTBi9?cy zkpV1&shl#yhhr(EGAJrHT<9`}^5B}7G2jTVL9o<>Ok5AOQW|x}!{t!<%UWOB3u7Us!=*DG+sb{IEUZvYr<@cD5=u87G*k8l%hSvK z+-K2C1y9Bd9t98ZfI1U^hBo^X63Of&^#_SOHlQ7K|Gk*$p8gTQC74QhV{k_7f4A@DC9g(-9AI&^sKP^BP;x z9$pdTM>nJxD+X91t-$Rto9_Eri&(5`n}=aKWP=OzlzUr^E|X9Lir$tmu^i`Z#UI+0 zLnv!DGx3;?Ha0WWqmrHkk-L8ckq2Ufs_(-?e}2?QkwSoQd^k{2NJ)&aQUqv}JW*NK z84S;itzyCH7UJ#X4M(KN&j%W|MjAN4s)^5;l#x-1vcU0Dx0zrSQj?i`k2ia;a#p*oES` zpDRmC!*?(!rLfFhz{;jzs_-)EZ7WJOI8ru?;%1lqg+q#uXA&j)m60$HU^?YriTV%v zWd~BUc<`v=uW&&#Xh1QLnN|*qhUQ^WtwUcob8PsA|BD*ZJ~LTr>jXMjQklCtz_44E zoGczqBgT+g$ul|stKrru1c*fY3y1_h$R{9jbAl4Lr%tP3cAbDsh~jcUP-=7kUrX9; znjDalHqSIQ?jIuZbzV~tgor%&i-vpSuqr})%IOW0(mloky_M7r*QC`^bu zqQqq1cP%j*g((KMNPp9k8!E7;ToN^t6^>VplxF)njYH{paWY7dFBKvZM+=vlEGd!G z9g-bdQ1&b}S$t@QZdKZs?R-}jeLZ1H;i{-=j@R-f0y}Jsrvs^ASk)P>7UhIFHy5DKgA04Yod z%F6CFX?8?)rus&%*~?W6p1sxWCV;;}!K4yZ7a%q==3gF|5BBzfA3k6mXK5^EnJb7xfPj+Z)V+%7#sSVHFPOtbg(;DDX5wthDNdK3KuI>6 z=XA;dDEVN{)7vv?4KlZbH^<5)r_ox(J>4KNA~}asy3JeR-pe?vl_I)y;C_q_U*rB1 z5T;6J1TELPklyy?Rc^|3+I9XI-;(4di^&qM$;ll?bQBv+%9Wr8i1Xc;F6t}x; z3G@7sSgh{oJ=IYVi-w656+{J@ie$wk5Vu%S1{3&!Xxo?|M^5(Bc`L8&?BG|sX$#bw zn&Jxq7#9_6)XFtlf2K3iy{f4(VnJh0Zd{6dExswfOGUDH2zXe*P4 z)i_Q_CKOIRTch>TbSUA(;Z+tnGFg8N$0Vu*`cy9QCI7cJLKzLDyew*tZ?>)ROhO|r zym6u`!Ny(62CVfCFfw1EWQ6ncqo_7?ArZ*EH)e$4IUQU)r2}HCppCZ~vx2(z4W;B{=6I7pGU{KS|MJh)PVI`y5WC+GF z2*zPb;h%)`F_eU`#6jc7acuZe1L8+GUDqe^qk89U#=#kMnJf|mao}6w)NMwblC5~s zG!R97V@0RaL3U}{Kp^!tCN;5erVRApNg=Xe${R&#qzY6hs!tG9@;6pu1B?(hzZUvA z$NQ1@lofh`OGi2y7;DDD+!k-Ar7dEMI%8pm2nLovDuQ@&p@eA%aQAn2!ubOBlT=gr zs&!!`zd}h0Lr6*DDAI)M=U-kyvl~W*$NwB@$WO-OX>E^cj}rH`?2O7cP2_s6$-NK9 zX%yA1t*E_B8p@(kCJz%y?zBr550+-EX0PjQ&S4Gf$oMmx2vfsy4!9&V(8ZuaKbJkS zpmbtQe6l~jGf0FhgG_+spt))pR$0(M7agVqk93PDP(dEqPpTmc!DbUa6 z{MUYVaDL1x=K!(#e0EwIFD)N|jI>sH`cHTLNNd(=I~b`Qg;5%RY=?3XqbPJNw-6@T9cb9ZYOLsR4(xs%5(hX-8`otUi?0xq8<$UJ4)-`LG z`K_7x-*eyYS}TE8s^49_KalR#+XnrrR$ykKVllDs-Rnu@{Ox{Biv=B&MG$qf42Rhq zA<;PTm&pu7q+;dKF}t~U!X)ZICI*HML&%J{H-IQ(dms`LJhuV);~7}~=wuhI@;0ob zAb5??-toYmtze;(nrGM&pd@7_nlg>VgWkyTahrnk*@nOK*;2pr**zfHkv;@FAfL_f zl+Pv|b^k4Xbe`mV;tUV~Ze~C3Bi^spNhvnPt%cH;~ZYt=(}MO9w}^|A3OO^qwNx!Mj`< z^FT!VwVw#tCRHQ zKgLp+)};v$OJX~E$x49Tk7a+{k5bU1dA{OA2z#S7wi@$%{*0$IvdB-Eqy!es9Aiss zW^U|p7>jjCIrDQw4hqfwN@xW&m?fD^WEFe)E&39at9P-wqV@Y$?#@~E8OQaA!FI$3%WwuaD0~~i{w5PTh@{CV@zq_f zr~4a6b8(#em@vZeu84%n-g+y^M<}iB7Qd-)Ap0Q2)r#~Nqi^vJ#O*OK3kcV@l>OrK zo1xe^^$M7u2uWH~5M8M;blnd;zm<=IL{|<6SFF!zxPEP%8BdC{@riPh&l5cNDH)G}da0D5 zg0`4A;$fZ-6F5dfmLsgWIURBFU{Zs#A{e$)8XORj;F(^{COP$lNd{xEI2^Dzu$oGW zT9{|eQA$wT*r%CD$T4R9^w6t(2V8!W3tEn)R%WYi#1mYy<-el(zQ}{}ve2Dcs?S*L zjfnp%%urTe!#0+-smWEF4t-kIc*6h$T|(A(I6eDG$6)K@@lclcllAd?l568DbJS-N z^`C?kzQe?cBIoBvy?8XU;~D(;;J-6?=xY1(rqE&_3EO9|4qdJR@IJ0D_6{;m2O4&d z>8G#IEO&Zy)71+&c52`8PB3M*s^4z6saku&#^Rxy|0RCh3h})f{qlTyk;J)jk>Fy8 znt8&bk@H8UZnkYAiha}C4976xyNCLZ9Hi+gZ1#7+Y&uc2i9_e+_z{0pDngmr8Z zC2EIz%%yo=tv&e* zYP_aR%6DXCWA+EZudpZaBV%ZX&dqD_rQ>m)|wNRk@x>h__jgw^J8q#g>mo^Y%yc zEUxSU?B#ppPx#tBZ3B=yzobU38~g0d#hl80zY>3KH6d0!yxM5AYzyVuc02Ls&DT4NeK^yawf9#hTWZX4)etMOtE zsJ=ee!EFRO*!e&Q+qVO3ew-9#eO0$kT{6NhtlIUSl-Rk(IL_U1RI`^mS12lqm)NxBF#U`A5h+V9eAH@|x` zQ%C6b>iAp#b!T}M1m$xGjL9O^XP}C^`cmC6f|PCXc*Oh)RI1+iKP_yz^dg{z9UfX2 zcv9~k6%#Fq>9&H8wR9}eD@|T9n~wgr(oQHo^2lJ(UKBE(XL-~V!@@^SRO$MpBP3p5 zTffuB+Y4C-N~e~G39tI7F+<93Z9ifh_1~0auhiqPgV~R>c5m`&)_C|}*zf7|doP&d zB9A^NLqdf1WimyNyEa3HrCq>FV)(@kXUPV~#iZcuJZ9^Uqzvw28Ne6-hBUu8w8%*P zrGgbMF%xeK$(W=T8%eiRzDHroXt1e`o4gw<6ee_22mx)l6UVMfC^tu++3unfGOhq{ zKbjB~H&CgNNw=~B@<&7wdTXjaK>k>1vzQn#+DTQuB&XIJ$aXSpZ#|oY-sc5)MuoCKX5)a53Z{6f#~klJ4K;8XlW3pO37s`iLM;JO5lK;=Fcfx zX}m^x0u@8na5;dJEdPg-biVMXE^E7hVL@aWIl(Qwb-QJzTDoglyGDtOr_P^u=`2E~ z!w!%nFve1#UB|{VS>|$(ECq7d2rA-;|22pGs?b2M@+pU1DBJt@{|ZWO!O@jvK7o?SgR~@q`o52}4d=jm zP#3KQ8{&C7xg-vzDOM%g));}xhIu;I2N+7yGK%n0)LHmEb8-1PX$WNwu1_9-5AsNBwzi_ zNpeXt2r*TmERraA-fT2J&rRa6pjvy9KhAgwCKOpDAVw#x84+hh;i|#2jRJ6Pkb!BC z4n%>bDBU$XVqhUe9`5u!ZXRJr%QA1v!69Meqo}4#UMTrUpo;jSC|%(5vGRA`)KSv| zSj6hCTnHN70dr__fG+ibL)!qGq3ykiK2t0_O9=$9d5WBgwaa(!@6C3v zCSQIYqWvp}E%Q5v&1&9POJX8LfqfbVE?%4F&~rs0+3FJ$tvv{s-R`L_AqjA*0uaPEr($#@AYsk+Dk|#BP+dDaaOcCFQLqGwuO*xaB&+vJ z{3X-tqEfBV3(yfQt5vhy8J{mapWO)euKHDQV|wo&xB*=yz+L-G z?tUK1nb6y-NWzb%1zEyHW4Rej+ad6PQ5>n}?*Os`6(y%=WW;fDQEvU+?PdM-I#FK~ zWjds_ty_NiWzQKXBGKNz?VY_wZ~a0+^0z%)X?)XIP-SK55RMwT2vUJ=G2e0Kw}od_ zh_N}(P=G~PRumDE2b+V;v*^n=8J+%OUGzW=T2o?IQ{v5vtM88ji&dTT=Gb@lfUn!W zlnfuyOBwO69=45WDbT}qQMYz_)=u+&&(+iZ*pIr*f&)+LUp?#=a1YxT=wZ+N)x!=S zr+IyFaCtPjmgVDfd=7tQ4Cz?0H%_BwNtS2_2+J%^L0uDBn7L@m6b;6a9AD|tpZOUs zC=?`-W)b9~4UFUvLcl_pSoocY*p&6w)h!*KI!U){iFaASfH_I2*GCQ1_`{`jq|k^M zBXXPWxPVP=(NENjO5YI+1{?^qdWO@JKs*8aEkzjD~qa9FjXetG3MOBqez9Jbn@9CkA#;`Y|>9CjFA zrRPeCNFPL7)kgyix>FEQ5GP4aY|*uG zfndz;JfM}G=XCN@B&@Ak7|k$;7k=V(cux^Juiftt_KHtmJ)r!e9EaDcqrCaC>y4L* zIAfIFiwP?Dmc-^xEk8YwfD2g*`ft3C1b=%U2{sbyxP@}O@0>4mt?ywa=Eb3nPk+a- z8BBfwmo(!vO#;_!6#tH411Do?j`2S+>?p{Qs2RcB4Uvh5ikXUs(YCe9V#?3Ftz$M@lh%Lj@IfDC> z(bJU44kL&}j;9(nFd~Sv5hX*m|J1Mrk$@VujWFHi_|AW+VPE`R!yfn*h^&`$=W(7M zDztLxM`OX~E=P9qWybRsw0O-QkJ8Uo_G?&osUEU0If5qF$u9e2l<|ofa)I?y&qZeNf7LZn#LI8l{JZ|Zok3Z@f`Ukx%8N`D4 zqS26K*zu{v_omNpgi{k_1an#&fFHd|iir4UrL?L86yo%e{tx$RG=eUPchAq1VpCvM zfEn2QL(9v{@#Mnr6#CHb!8>o2#22;$x!hFlO#wMtKgMgeQn_^M3A$sJAkHW{vALLd zQ(JfGK6yHJHAI&FY$hv>x_m6Xj<1j^5Gu)Ks`2795&LW6yK9!3uxIN>5=Fb$NZtCYl>t|suH0r1i_J7E;zc6`gZ!Och{{h zCmSyy3krUo4*zX_d^%TSRcxUr0rR8$e=t8f{$qYLN@bhB@mp>CHfXoj@CaE%X(>C* zMSw>kf_6G<<;o#Xpel{jy+8@%uv@@6><}O%oprb{IAJM$0x1aoRS^7HMxw1|XOs2g zkn9&I@7>kHNth{qolIr6!)V`6N=eRcjvs-VCLZsdMg0|u_>Pm->!|6t`MOKIlAyC8 zvKha4o$T`p&du4`Js?84$CoTxr=^4x?QeCfS<=&ccmSsq99E0WHYWQaCk!p&2$7=O zy4d`_n287_ECdyiut(ixhRuhJhqe3hDhOJOCrlDJc>Pqmsip|Qm}Dh@Npi~rnB?w% z#w0K3ZTp`v$uR{ZBR|nMM{8c*r{CLSogw_<%`>eVP>3S~g^Q&7dI94tq*Ahp2pQ+J zI|uvB6@(Juw221IB&w2Ha{=2ggkQrL$h0Z%&8krJydG~)pwvuNIdhH6R|cxT$;g7` zq|?+`Zyp@97?K7B_%i60DaHiK4Zaggbj`5OgejVE-S)h~h5YLc}ZNG57|dJL;RhBTGK#Ugon#yl@=@&ZE(cNkER)+`h$wr5K%s!VsuvORnO zR6(tP%Jr4c=|%s;#X)Pg0f{t~e2!uVCmf!JG+O862>5*Yu+^i%8z0VPBp*-!b^U5O z%e{Zoi3XcC7%3Od3Cp*WT2VSWdm^Z%tw!L#vwXrjIWr{gZDQ|+Mie%}0 zZfizP_pHdEB3$U*r`^GgB8zE+%8h*xfg_n6gLaxUxN=k|k^V6NQ3=(Y8P zv*TCamDYGE{>er`sO*9#`D0||YK8(m7J@AcSpLY+42)jP!34+g(2D;gf4ulx{y6Y! zlVBI~Cg^eg?&2r=H&A+aE$?2QeRO?FB_Vij&wRktlWMea4yg`2@mH{3Q@C)T2~QH@ z{*3T>co5nZ^K17dyQE!{OEv-NPDhFVXXd95<(tW9uteqsg~=Sx#pG58^^Qv zkj0cwu;e8emX!Smmc-miRR2$~22J@jZ}wv3|KfM>q+ZYM+C+44E`$Cyip0yyRt12=zoXB~p(v%$)l0(uWL^vg(sIZeeWUq^%Dl=W~;GkAxkIk8a05adKty z6mlb#B^*A)(lI^!0-EC%`7(V!7C8V1B);J%Ot1s8^p69Q|8EDRQAw>!i$_FG7U6kk z!v&=J)ET5nG<|xg3~U1Mb)x8BVW4G)geL~Mm5KfcP9PVc70^@Nok zc)bBJoJXlP&$8N0aJOfj$(7d!bg}8cU2M)jUF=<;i#`0WF1Atq2?*_akKyrBstpDq zNo_;9R$hkbH#8{#h9(!m(Bu#RO_u+GCV@?Asl5Ihnj|p7j-IS~^lGt4uqyCV$3T`I z)=Gb&EGHi|5%SEQOCdwuyl*|wq8jlN%1o{4>)~ad`E;g^bJt=@axEVtek^SSGKa^_5TZ<|=E6S$6f~%eePfmXd=PtL4 ztS)YyX=1eHBi(2&FY+LK6!K7>kSy2BZ@HZ2&A0q7AKqk_X1}u(86$BNsPJA!S*u#l za(q1LT|KxxpLP&Mrx`omix&D;Fjyug2h{3GyQw8=6@y}%j-@KL3OgOu&o!@c2 z)b97cNbUSAAhcqKBjm&CeitL#&nS{Z4{g66b~!h2ijnCUMaj9bz2Z9n3Q^a1M)G1} z+v(NkFBXu-{%$E5LAjEzBiwx4oCBMWQ0`fa+ywLB$uGjIbGlxshGb=|ms?e_&vVB7 z^niO$%jxu@9M$AbMjwYnaF$ielhRRhZiZe&-UVcHIC2!6k2lP-i$4$5Jv(uy^9N6w zlm&d~>v5UhO=GBdW5EXLJBLqpP0wB%4U*f7L}NcmAaQ<6AWi)xRsG{C?X8nUmxpIc zpMQ(K@Rky8)b6N$w*vjBreMqbK)TAMx)a%}#AV^Y*TC6yVKSG~+sqMeYa2o0IK4uc zPxV!8`V-uk?roXdkW)pOAzkaE?chQD`I@5^Ej3PYv`AqA+LXrwkC~fc1@gf|n|o(m z4+^@!a_i|mx}(}Mq~*u^?9(Na$GfRlAuD=&28}LT+AIX_JeOB=8!`-%Ca@3mL@OSh zjY18kY9#CKwG~ROQ)_gBsEhsYvo7B;RW8rB?mrx392UCew|Fv?dAD3F(ey`b>oS?N z!>%DThH>Q_-^ztv3y6GUr|XuwVqWtdzq5Uq9r>_g<7T~7Yuj#qS;co-=bI5FL;PM! zli!!y;NUAIEKS9vIgdk(%W`l7B6Zp4JyNQf0Di?^7<>Ih(=2^NMG1pBWx*; zubGNEPU+3dTe(Y+V>(k7n@57#{gkXiR%sEw(-M8NX!YF`lpoOB{&4@68cV{U4+?+j zZ`4t=nyn+Y;tHkn<;{bga`x)Cx*BW10hx#~UoyTOBDgcGwp*N@np%ZtMTw``KcqJZWLJxCqw%%C65nJQd{UyWw zL?VOBua%ljX@_nRkU%aN@Tf43ZCb^oDMl;^^5OSC=rzLiulk}#G356`Y$w0TL4bzh zZAhW|BD~Mun#oX>=@Qo<8qC@=0v|nrK~twEVi9ZnqmJ%*BqQos)tTi?uXL-2*B=vP z;U4^zIX7~5J^@UAZ2n4ZCO+(dCZkm&jN@1iA#+Bkp<*QAVB5T}<1$_cL8Ia(d1ON3 z@sC+h&%V1NCUmMb3N*_O#&dKuzzWcPhS`~3|luILN=6$vSbRuUJgM`t+uq4(4NR!A*?f~U=hO`m_Bx`juQd) zK~4ZZNTV;N<^-^dI*^MxYqWW-*My3fj(6WjN`7L$M-M$H+K+&JbZ3m6fVVBAF$pHbYs_A&((d zPJa3>N1!7E#va$1Sd+MXBv{N5w6Vi=Ik*U}I<{o%84^NstjM9*RRk49fcljfC$(bjn9Dh3 zKmrg%r98G52pM)PiK>TB?#rZ=nn*{bX6-iRtzb)MXHflxm|wrBWr z2Lex;kb<5Fiz%K0V%PAo_w|Nx*H2w_ek$~h7%_X z9%#WD$eAY%WZBzv;drMv6Kqy}P8>}DuN zAhs<68gUN>74k};y#ycwguK3*SQQm|cCjDPCj~eg)Vjla-2SZEj3sRUv z=zDLe4Ym*ssQToR(`X$y@+bvJa~dTDVjadP`^7QsU7+QlN#%Z~L&H@huyHv>6H*TIFCHUzW|M(S2A;eVBExE#2I6Hc`W6c1e)xi zG`3@sxB&&^y|Y+Tn>+an-9~{CyohqbMgN5SU#V8Uh^L)W7LCR9RsI4(=KP+)?xb_J>0gWmt|8rzS z8_d*9K7*AXwMaA$lV%XQlN9TjduCYDfJ;VV8O%6@@g8iQXNnl7*{zI>h^>t+pk z*p=YiQAE2wAdW{f?`+MO6(uGXQ;)})l|!kwq@>jJ!f<{emmL8^k0MXdBl5KC_ym;c zP<^LgAJKMhha_XFrv0Z_2wBE`+xMCv!r+zAS=pboy>(a{CI)jbc{-4Jfn6iTMv;+b zA0oYf?Ce&J@$_amj(XK0>HWh!P8_a_qt3K zEz*kQ9BFGg=JK-uGTMJDH8hMBiyH5HR@t*i@;G>)gngb#us_6qd1B~&O0HWV$@>E3 zJbz1CHrX~oIO2MG{UwZqLVN@By2;l*!EJ=R3#oR8DgGIo1fKmJ`v)?-Zig2L*rh7V zKj>vO#yrsrMIyfkrNMJx5WF=lWyP|<%fb&+=o^!EiBbb-$IXA!jvI*f6ym(Zt}O@g zNC%;BqvZ@RUWsYLOMPdcP_jAejY9YTrs)*qT=GKtKvgHgph)EEj$*63c!pb65>2c~ zl~=Y;Cy6Qv-Wo2&g1ox#dk1NHmawbP6>g zlz;isgvDY~p}Hhb7W);koJu885^+jCGu|?vKlN$xuE-ZGuRVGNv*ASFsrc*mR(NeJ^UJx2Id~={}cBZ^#8^^ zPAU6WVKLxQhi!hx3eB%4lEUp~8?Mj4F|Ve7l0>>=h{m%^!F_eKPWUd58Or9HCyvJT zlU-pTB1H|+(VqH}qbJTEKO#w}ru4-Xo|oVIX^LGw`qMH42EL z(2O-}>xCabyY>eCMKebgq4 z#C@_vo;=J3(ODI_sHWH|u#+XElP>w;{{Mx2R8C#g@2j&X4R%r3uR1pQ#}4`9w;fWZ z*zkI-;H0P&*C^1au9eZj$wfmQ9|B*mbPxK&MLa^HWgiM&o~fl5y9jSNlc(Nb1zn<8 z3_GrUSfO*%?1qGnVP%qeiLFu4Jr*v9FI+#GBc6d0ag+$Jx_Byy%br!HWsmv<1xVen zg0}~2SiJ-;dk;zmnyFh$(4%hGpX$XBdfkE?G3rzIn%u82QF>-_^FwGe$85g8$rqu# z=)m(WO>PG3KU`;kg+P|3xQKmJUh8NLMX5tFJiG%gY%(ltI(miS93;)%aUnj1x}~uw zro~wj)$8ql;g2xMS_4Qh$!Z?myJXZaX({5=9H@hOPw*jGb{w+zB=BFd8kKz*%lalhs)+GK5IfPy2zQ z5mZoqp3r_zB^<|&E z5BGZaWwn#>B5#T27>HflIyfp~Y79v4CaBR{8t*_*R}xV(4U9?CB7ZzhFBmF7W~!?Q zMfehf>QO-_Qu>E^B>wdSgWxhmV3t}kk5ESDSS~trI5;r?%99$+=%KI4$i2{RHOohz zl(qITWJdMl^?K-+Ry5P9$l^)9^$4lM{Z`ahsFWyG^{odYw+3gG7*@Bb>j>l$$D(*k z4d*^6Qf|w;B4Z+2_)h%bfu zTl&{F_IsLjKiN(2_jkNTOH0G;X(AOhj3!(=>FvDR`hjeFfXes!0NK+xfqSQI3Ez2w zNs(LT8PD=B%SB_9SH6q0>gCfm2QGjZk~hCB^#V#@tba4rlKBh%?*Is9_xAPsY@IBi zcU|vN=jkE1{KAWZhlu}>B~OOeXYp8ggMD&t&7sxhu$hZE=5qav5><*KD7J55inZ*^ zK?+X(N7DIrgG`Ldnqs%X$4`&?S21kg1HYp_#Jq}_)=Ng-IC+G_&HMB?_-PjaC)MpW z+faJLc&%NS7q^?lIyvn(+^yUN_7!6D5&0u+PupuAk6piOnSK;;P&(2@5onJZocCT7 zBDA8z-N+c-(@EaD z!DeIb#ht-}b?vGv9ek1sU+t8PSB%kuM`323YmW~%o;SM(v+O%nDe<%3`DIregD1rI zbZ#-)`7vEIhzxn?u9B%JsA2q+OYhFbvd(krJI?e8hZC}BZd>0SoquXqB90DUa&264 zs!wgx>s{%gG#Mn%{%Q3I^bY1-cy3|*+1JficW*N@bWG;6NzECTAMP6vmVu|K=C=p; zddIJ8Cijm~adWr#jhx@6=4>vsPcHRuMy6WjUnM*?8gaJ}NGu&EBl3mNA5jqwz-SK%`xJ+hHrZ)2AFR(%i_0hD5qo zf=3{H{MF6Ff#d$FR6fb_-Pv*Bf|*0j{O5f++~`t9Top8%aBWnfwh7t8T#e*DV;@~+ z@g(-Q0xs&4K@NLe3oMpce2+VSsUeg29%^hSEfjLz9kDoecx-w0N{0D+bF{549Ga7T z)!J>!#5GoCT*L2mscO_v=>s84w7J;L4EM-ck(Z27fA7o01tu1tx#G>+ zjM?L3{uK&gN{bd7Yu&+8n=m!_DcdoJ_aQQ|h@N7Lz)ETnmd?XVtrgQeeCotF#bBel z3ge-O-kN$sEMZy=k-2>v>bAUb7%v_Ox>?o!;6yt+RE4!6W||#e6gpv<^jOi+lUeg1 z)pn7m==ByjdcDkLBOTUH%LXfnUcrJRq_D`b|^ewnkD zt3OEMr#xkhwT?_i1gupl^Lz+>ZJg+^)Q49X;BHAJjZP;Vj@2We03A!7R2>NeLD!9u zb*&5_Ex(i3qrHhAxjmPbs@A+bXdVuriweT%niv(I6=#;bkLZU>rc;LCY4B<=J}0c1|4`^T_w0(@;&2+Hb~^a3wkGyo*#k0 zKB8nF%Wfwf1uP2&V#NBDMAH@tYEp#ep>vLtLyCWy$J&7vcOd*lePt3tONa)G23}l5 z+nVltuMfS-*J1g14HX(sowsQviJob(?WVz-UKB-Ja}qf&=5p;ZP;wQLaq)3VVBz$Od0qUwdF==^ua*C5Ue8B5j>S}8xy-M5KG>k*QproQ zPQL<}Bvu{DNgpg$$KcW#48jRY2hzwWKQfKrn0-I1#;1_mwdPzG9u{ zr^iKg4;Jk(*lz*AhHYztw#VEnliEVrM9?1#Kn&Nxg-UW9rQ4@z9P$}z_7!);r}Nf* zxuUUJKBO>oFzgJQP^A4BGQ&B_q>O*|RuH_8<0rSTJ%H@)UB`~bRq{`xG_=M{!QZq@svuo#eV*ngJja@u9jY;XI2nvJ&z zGvvq4G}S{{Ue)DiTO@L&d__#ldZ!+2?|3X;P#9RnJ2f*-v%$~UNkkg9AuuUe9-@XN z1xe+V8~4O79;8$$-@f0U%F#vQU_(@d#jhEF=ct9^2(X&n!kdB@&jeQ06V67nCF$u$ zprRn6#st-^@fRLl+aHgAT-WV%R#E+*QIBnck|8-L`j6|I>lo(7SI|WvMBo%PYZH=1 zLGZ)kCJJ13Iye?tzCdq+yHHdUn)lN^r%3? zk|&R{6pu$`*Rae5-C4q|U<$N;#NVVPywL5L<5AQ7MT{V}FolIFM8?v}kKJ|_ z?Y1}Wm%b_-R{KWwNKwI01b}T$R7+D;MMe{?U;KVrpqdy)btFU#_#aPfaeq6-cYpHTac6K+v5SJ#Wt*7d*O28e z>8QX}eq#T?)lvo0AUJmzcjKEq8)UJp@i( zbN-dQHas1^{$Bpv>*9Mz3(p%>w0kUdICWyv;-(#Oa8lfJpnFWZ#@;S zYuiRT?B47MEnLvd-rONUm_R~Sp+5z$0Xj3?oU(W#0Et$bmOXCcEf}K&RBcIK!+;yI z>>oGeB-jlZ{+AmvT<`PtW#hx%Lx}4A&kHQz1Gml%HI+u<--AXLy8A~(urABmfLV%6 zYa9e2@tV1T+u@iN0^rmHxVTKx(j1|&#ztTB{zmdlj@*C!+#YB4p5ebj^ouWY2MsV0 zOVVoefrH-lkS0N*O6I!I;h&X|+{-Zatt~F%sZ-U*<{$+j1#7bDxpI~-a(wxO1P(g| ze9uv@D8Ha-Rf#SHtLq576#O7VHo|Z`Twt;2r6Toqe2}KVLJ+TXGN11v;R})LIN>+U zkbSF*6KZ@Yr6>9ZNwMMbP1S>*&uxdt8Tv2EFaI<05!3nR`{2WN2I}C7{1A%*pd4Gf z!YsqvXj6fk1aDKoXaX}k6jo$C9xXZ)RybKd@C9>jKo^j;nvl5=x!?hw3ous4Nd+mM zbC1!k5dSmuG0@s~?`-u|XWtKI2$^)qT2qe(fG$K;gv}RodV$qV$7uvd0>}o_xpEV4 zx1H88DaTxh*D)CxT~NPa@ID_5igHn; zRHzy*^!+U`!*^+~aejOtWquCUOd1u`y4*^xb$I$Z1#v$@lomWSua|)4b;0lEHC4h$ zfyH_z2CAy&bSj(kJ*~xdaESPFBAZwtgzEm!T2oGbneSV&M5M5!CLXa<*LN*0@4P-a zU7v5_l2XYJqA~%|Yr5aj>m5XbNKaeTKm|hOz`HCXHs5$f5i99JsyOOU4E(T`v<>5QY!d?z=2ak6fys`f zin4k-nZj(F)9&j#3n>TsnjCM##U>d*y8Ur(ANa2q#o`9kfgr+*Tp%UbPf-#4*7;WI zVzOppH=(_No#OO!=rxIJv?Bb9*W)Gj{|)?zs%XDscDcLmvbTFf*d|a~HlFO^VLqZq zELT{R7oTDVFrdO8%8dnCH}u5(dOj>4JJSdTjti>25LW??d`PT^t^M`Qs}0x3!Dq03 z@us3N*IRro?@(#6b;!pd5eEV`EFZEQfQbQHg_3J8E5HRqJC%qHAe@CoW>xDN0z%+% zvk!2jw6FCq1-%4Ksycn`oFEd#H;n6Cei=~0ut|^;1IIt2UoGQv#L~2Z{It@6XMXFc zOm%!7K7M&4R7w7deu~M1G?Tu|K7!~~{o64l`hJQS|M&PKQr{YL)`ROa-^C>u6%c#U zV9s;)mQ;>X=?e5JW0__v;E=BlJ+TRk4KMtTPKj-*_1sTw|L2GxEo{DHFIg;N?8u4q z)?kX;+_R56zUQ;ArFTJJcM|OW(L@55c;?O3>e2sbBAvmS$mPE@k@L7EB|8~+qvcue z*KS6+A(A+>dU&VwS{#8>MNuO}!JTNO;R{g@#>g|i8gKrE!7N&hhh0rCvx*|Poy=HiXO4Pmxr(8uZM5qZ4m!o;YUYLpYsD|^jpu{N75)7_1CB_I_+-f zN*ixvGJCKoa6LkR3C~kpfnNxp;NN>p+wcK`w*Mr>Sbqn%3?8Z=75L35@VW`Qgj>Hq z>ebhVpbPsSvyZ&v4evAL9bK=W?0(t+xFM0yqUYBbChBo|HS{8n!sTo*pNANxSxbYg z@m2#e8?9VgdxJ=`zZNv#J`?L02-gFTqFBOS{0VQ=NgaA z8-zr|(KwAz3mSwe#P@Q!z!&o1xYpKyr`>@D-{0xHCHeID?$+{Xvi~Ov0AKp#di@Ds z)Bo&T;RZmuk)(dB4~UBYA$$$IsPz=SzEcWH`W?PzL{;}WtL$*@_+kkC2*-9)%ID7} ziQOM0TGVva3(W&Y7SZ#qriHPCtwLuMlwfO4Uif&T2znj*h4T-8i4+o zm$PY4NN9n%#iO0`2ShoepE|KTy-834#f-cjM4?)lbgKKy9zF!2fpT(UcX9493Jg#I z2V1*6l>l3_n16;Da9t=abI5}s-P2hdhq|=p3ITMlUxbjqPC1jmH8;Dxp3uNiq8vBC zifo6X+$eYOGgii+x5hpChWy~dr9hx;NKF-ig+voylYUIALTi5l+&`Yi8td=!m5H<>HblQQz~;~4zV*Xm^jG)${Ryj=S4ToSLPHnl<3hlRq*ci1%C+@b%YO{@ zBIHBH!PGXUPKaXo{XvO4DpPa@T%7W_5!DzhQAvCxI^4`csX|-OZS6Dkve6tuVHja! z3sOhmN%H7wl;~394{Sf_bkcx!u8=SNfj~JVh@~>Im)#l)}e&S@vp{Dv76}WwE z9r&;IbqmnG9u{1{P5IrvW>oHuYV+=J@v2)}dvPmFXQ2|V)#duc!DFLbY&%93mTk}C zamr;X?UIf>i3&Q|o4n%#FM;+o`#+{>JIYGY65#F|Lo$7? zPp9u)k2i4M4?engyF0x^8_ah1wGm+Mrkj&?OgR0Ed7KNN8%!_%B`j488wzkGD851xu*ni~W$ceD z=c(d~|E0jM!*XadYn7D_i9d{;2TLr7v93_5pbOzvf5Z>fvT_nMvqyYY?C9uu{Oq_P zjn9lQxE2&KUGuT~lSR`5;Y6Xbvy=8+Y{ua>?#mCR?p2%y(e-=YCrBRA>gtRpmum-2 zy0s75Owao_*Uwl>0O+wkCsbX(~%p5-OnZKpnVzSa{Zf&<6aTEobNovL0W^$|4uN9Q^Q z%{6Wd_Zt$+vPTLomv4BuXp0N)3f!mL_6jQ+zsY`Y__EFJlY`8fLj&(cLv;oJxOH2V zwl)rguQ}QdqN9NDHO|3`Ig5gBMCT#KRT`>C*df9i(&GtBV{)&>mFH2L~q>R-RTPUI2&TQg5 zt&Lak*4xdHpSxl8x>3HPVaap`cSvjIb3EXQY)ifQ%n(z?ypYQ!fTCaaG2SPb+m9tg zb)KYGnXhZw^>GLYU$2abK?kBosCX45%`xpYqDDN0ub)4X=~e2h7)JV3JEwrl*I??g zsp~6k`u=a~u_S#SOg)lvJyDN2f2c>VKh)!4fpEc4MaJLMBjGDB^%(sTCsq4hY~7#o zHNS$6P%p1)pG?MbwYExGm`Mr&|C8lW&mOtkbX$wt&uQMYa@E&#{5&a`qqc&7hZxj) zo(Q|_qL}+xwf-fRa9y{1fpqG^yVi@;Kc2^5V9(?JJeTllhO=~JqmmaUzZb0E{<4r*N=JArxT<8a z1(SkVl1RR2re>(7=8m`GZl}E2hlMstQ&jF4pu$l;Z{dztdW-%seFVn+wM%B`u3!G{9I4=5sQOv{hSw5 z)nd>}vf2VjUnA=14FSC4*v~Dw-CrnyJgQ{YLS`Hj#yUL226S-azg{4wrU{uIiu^Fa zGaWHc4O6a(7%+ui8}thEF~J~nmWO$pO!InX{zcHhm~*r#C>cQK0Jo2(6zgr(O3;@Jj&Ga|*L^&uRXX<`Iz%&^$6#HIPt;Auozd z7yR)&&bs6O3afwcM~y}5=J%o17$=? zt?ECHM-}+>{qSCV(oDRqI!MYn6iP8*o26&v#y<2w%96!@jrKHbYKn;Ql#;2Bq2zQ- zmAuFM7Ck@~@0?^DCiOdco$@<*&E^}s9P&5tsCwq&twm~-Pyp1mpk55SfRon~Msa;p zZ*`IU3ko8BC$E1?9+Bt~5JH-{41F4VkhfM~8s)wXVSkejO<>%R#+YuDA5FrT3x!Ru zmaaV^whX+(dX&i@cpkgC2<~1FR;kO!e?2X*XP1&MhNq=N$aCe$N2O|Jh+95d)^IAY zual60b*N0ehZqe{K(bEnif8DGN29^RDvUHsRZCZe`4SJfkVs_+pVLtl1{greA;^JQ zRhGiub#;`aL|LgGS!dW0dbwfRd=w)KnB(EMB11Qj2}p0H-YIgFFv7n9%GW8s%h!&u z)fk4v6I>q#6$lMU+;vaiKCF%hf)TK$P=-~H};-+vv0HP)KWJ=fZM zKI?f@Fqwh2XXtJ>nNuQJG$u?;=RTDd-;d>v+6<51SkCgd-= zAMaP(H`U4ks3khL5XIrexxB01&rw#1Iu^;$_wY+F^)~QjWT9#-A#`MGWid3q&Vg6v^hqp1#!d95Yd=)+ED50pwZR7iR|aKjNbXun>TM&Jvkb_848pkQn1T;;c~8SkxDNR)Dn zzg~Y?5~1(J(V!m2&D(AHh8N||!1Plsqo-%grv=@1KSoqj7R=-(6TUV@;7uK|r^D^e zvjt*1%FH>!>vsO`Vt>aL7km6!^9UX~VzMiZ+KPa5pq(s>vx21_6*_|@Y#-;k!yHc% ziv_|RO99-G_X;9!IHxlq{atZ_(drKY&8P^`}&eq-w^d#uv2c_>Rzz^U|7T|@-htyocHit#u^p?^fNc}_nHu~O^!r=5JD z2rU?wUC~4MoyES_IDJJz?nQ@Pkxi0^gWxE4Xd$A*tm<7?fcHWv8(QDtKp(Y-obpIT zCIuaj3mqxlPeL-pu1EOR0M$PDs_iAkUOGjh)a&2S<7a~8_&j_N^r+@bK^z0@j76&{ zqZm;@a#L9LK+EWx5WzQ{E`7uLDV^F1t_q8;5S{d+_*S*lJ!I3D96d+$A zTge{-VfEGuw4Wt15u_04EPpvqzm@nE0s)sprdO1e&LWFjT!{5w5|5J4#N*2ifj^1I z{-&?il^rA*leqszJL{C zOQJ?$)Vxmv`|EO{z$B~U48&AEf82FI+xTH0t)FThfzG{v1m{Y+v;#wfrbtxaq>7FQnnml zHi=>IYo~~tV2YRe7M9`pEQqRjj;4UQBPFFp1JY>r&5mshlNBc=V}?+ zH)fw|-jARfc3(VGVH$O0o=00~*W@EfW)P%bYRBdDbu}C~T0v;;H|0vN^D6q!FSFf0 zD>2E#w(S0ww4?HG+EEfjJ8FqP;L-mF*3s&jb^HL0g)T%7`EoFiiK;LR?kvy7D^f|O zVbG22i(~|wSPF@S{=1}x)c-Z?D9&dWk$d-DXR6CI*4I!>CD=s4&lIX8hOh9DNmhfX z(<8JiF$8IEx*SYn6R?i!5B*q^4rI2@P5C?|_{qT&QR?kGnwZd7yV*S>^e2j9??%xs zOzIHX7vmOGt&|OJ5L)G6T0+vM9q*}g`;1YML<>PP)EH(kL=BlPP7`za5svlE^x}K? z!{dQ;m5KLUbe}U|oStrau(t&)n@nnQiiDKI@k~ zWXME-vHoTx-dt3Y^S=LTQlLn}!Y6TDecj3z)WUY^?>n7pV4qP}f?p*5J`;hcn7d0O zJfncVz@V;y`+7&{1+(Fl{EYDpJ-lxNgfARQe19(ka#F4be}X7v;~;RN<}Z3@Usox@ zrK9e4L|gVpzoJWJ5kQ(KdTn5<+~AEb>i4=xjvCHda)-B80Ya!8Oi0N5UmcIRe>xts zK`Cr8*#GQ!G#5W@0nLG`_c;Ax&@L+* zs9}-&N*g!2-^1QL4n;*W%rGtE4SpWZqPH8<9K8Rc6!b)eCXEGT zuxDyG4~%CS>7Q)Dh&Atgv`gBY4mF^Id-oVWrF5%a^=GEM@uKB9O1CXl* zEApvVjo;Zgdu$!lEXFS_^r-`6T6>=jFXX&wmkC@%g0jw%)06wtH>+EYr2_ z>fxxOy|7}*hR^ZQo!WenqgYbTk?v?L%i*el?H0x}w~_?Mz|42&IHrNLx=c-AQsYTt zPwJ^RmUq^zy1nE|S8x8s#%fW6wM#RfSopvm+rk@Zd1tz{)HYT8{NPW)y^3$$-=7jZ zGLFcUGh52RvEo=i+4$GRSB_rJ-?gq)7-r3`6EX@8&-QP3G4c)6cx2Uw4`4G*i=I$E z+4kFf;0UW*r=*;w+^XNpotN2yvLxi2#xeTRy5e1#qiQ6TGR?Vy_h_Dakc5R~ZEkZB zJs03!KeRr8?{lvji8BE{VSZz#HCj}l;bv!Ds%CV}uqtOZlo3x! zgE60I?w$tE+chJ3;cXdN=)irGZU~K8m+NkE=8N^8el-S@R39EkT`tpWj=t+~Tt$pU zJ9)@j-cEW1$DR$-F5|LG?(gNTUW_=4y~r6+?B5u&b17)KuY9mJyV|Rl(<}X|7E$lW z$@<{t{QY75@-b_1usra61zENf346yK%f*D0+f#*QLnH%Mo146h)WVBOGy&ocm>!U9YXS@Zl|y3 zOKdAnHl&E+SQpvrO4ldrL};KaI5rzPg^9(3$^x-4GZ((fq$}Fp)EhgchrUmd7v;x} z8lH7`Te`cUiJ+UtKPrfZy{ZierRYgYUNu z$74u0GgzMb#OE4hR)vkPl)gZ68Zap?>v_$J zwd?8}dEN1WflPy-3ibyk3(?g;65UvZK5I0y1cbRSiA3-6!KfAC&NvYT!<4(2QNi+O zV#jBd{=yukq$h69@Nyo5A#c%IVWcI{lZRGuKp{4Z4}~=({uB4IMGmSc?mK%Ka}bwO##X*?IuG7 z0DMR#OL>jXt*4<`}H3;JDmA=9Q2&7cwK~;1*Y7D6O_c0FB zoYs;CqBk3g((uLktTuAkH@0LIDBL{Nh4M9;B$Z+7`CGHy2uj&xFKR)kMs4+z6LN`* zmh{8T^$T-y8O;F>x*)?`3AQW{Vw3N0v~iWvZ%PV-;XiZ=Ni0sBqCt!aMx7)t`4|CV zKA^>{c`8>`o`q}DJ}->9l8QV0?GJmNn&k%-Kjkmi%;wY$3@jO5|s$A zTR!Roh8OF?S%?z#ReZWT2R{+tx6q(=T%S4gMX$$$rN`Df<|%@?33@e7vZ^4WkMkr; z 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 J+?Tc3{sRn!obUhu diff --git a/x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb/data.json.gz b/x-pack/test/functional/es_archives/monitoring/beats_with_restarted_instance_mb/data.json.gz deleted file mode 100644 index f76e7ecc64b273b2eba552ad2eb7f5f59522dd9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187819 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 diff --git a/x-pack/test/monitoring_api_integration/README.md b/x-pack/test/monitoring_api_integration/README.md new file mode 100644 index 000000000000..5994273e26c0 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/README.md @@ -0,0 +1,53 @@ +## Stack Monitoring API tests + +This directory defines a custom test server that provides bundled integrations +packages to the spawned test Kibana. This allows us to install those packages at +startup, with all their assets (index templates, ingest pipelines..), without +having to reach a remote package registry. +With the packages and their templates already installed we don't have to provide +the static mappings in the tests archives. This has the benefit of reducing our +disk footprint and setup time but more importantly it enables an easy upgrade path +of the mappings so we can verify no breaking changes were introduced by bundling +the new versions of the packages. + +_Note that while Stack Monitoring currently supports 3 collection modes, the tests +in this directory only focus on metricbeat and elastic-agent data. Tests for legacy +data are defined under `x-pack/test/api_integration/apis/monitoring`._ + +Since an elastic-agent integration spawns the corresponding metricbeat module under +the hood (ie when an agent policy defines elasticsearch metrics data streams, +a metricbeat process with the elasticsearch module will be spawned), the output +documents are _almost_ identical. This means that we can easily transform documents +from a source (elastic-agent) to another (metricbeat), and have the same tests run +against both datasets. + +Note that we don't have to install anything for the metricbeat data since the mappings +are already installed by elasticseach at startup, and available at `.monitoring--8-mb` +patterns. So we are always running the metricbeat tests against the latest version of +the mappings. +We could have a similar approach for packages, for example by installing the latest +packages versions from public EPR before the test suites run, instead of using pinned +versions. Besides the questionable reliance on remote services for running tests, +this is also dangerous given that packages are released in a continuous model. +This means that whenever the test suite would execute against the latest version +of packages it would be too late, as in already available to users. + +### Validating a new package version +- Get the locally built package from `/build/packages/-.zip`; or +- Download the package zip at `/epr//-.zip` +- Add the zipped package of the new version under `./fixtures/packages` and remove the previous version +- Update the package version in `./packages.ts` +- Create draft PR with the change to run against CI; or +- Run the impacted test suite locally: + - start test server: `node scripts/functional_tests_server --config x-pack/test/monitoring_api_integration/config` + - start test suite for the updated component (eg Elasticsearch, Kibana..): `node scripts/functional_test_runner --config x-pack/test/monitoring_api_integration/config --grep ""` + +### Adding a new archive +- Generate elastic-agent data for the relevant integration and archive the data + with the kbn-es-archiver. Assuming the data is extracted from an elastic-package + stack: `node scripts/es_archiver.js save 'metrics-.stack_monitoring.*' --es-url=https://elastic:changeme@localhost:9200 --es-ca=~/.elastic-package/profiles/default/certs/ca-cert.pem` + - `` should point to a subdirectory of the `./archives` dir for consistency, + and since we're generating package data, end with `package` dir. example `/x-pack/test/monitoring_api_integration/archives/kibana/two-nodes/package` +- `` should only contain a `data.json.gz`. make sure the `mappings.json` file was removed by the script +- run the transform script to generate metricbeat data `node scripts/transform_archive --src /data.json.gz` +- create a test case with the new archive diff --git a/x-pack/test/monitoring_api_integration/apis/apm/index.ts b/x-pack/test/monitoring_api_integration/apis/apm/index.ts new file mode 100644 index 000000000000..e63049fdf9b3 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/apm/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('APM', () => { + loadTestFile(require.resolve('./overview')); + loadTestFile(require.resolve('./instances')); + loadTestFile(require.resolve('./instance')); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/apm/instance.ts b/x-pack/test/monitoring_api_integration/apis/apm/instance.ts new file mode 100644 index 000000000000..18d4132fb238 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/apm/instance.ts @@ -0,0 +1,41 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import response from '../../fixtures/apm/instance.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Instance', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/apm', + getService, + }); + + const timeRange = { + min: '2022-12-20T16:28:00.000Z', + max: '2022-12-20T16:32:00.000Z', + }; + + testRunner(() => { + it('should load individual apm server', async () => { + const { body } = await supertest + .post( + '/api/monitoring/v1/clusters/3_pOMySBSkCwdyxxBdDbvA/apm/503d57eb-6f1f-4991-89c3-34250cef80d1' + ) + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(response); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/apm/instances.ts b/x-pack/test/monitoring_api_integration/apis/apm/instances.ts new file mode 100644 index 000000000000..99e16be336aa --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/apm/instances.ts @@ -0,0 +1,39 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import response from '../../fixtures/apm/instances.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Instances', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/apm', + getService, + }); + + const timeRange = { + min: '2022-12-20T16:28:00.000Z', + max: '2022-12-20T16:32:00.000Z', + }; + + testRunner(() => { + it('should load apm servers', async () => { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/3_pOMySBSkCwdyxxBdDbvA/apm/instances') + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(response); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/apm/overview.ts b/x-pack/test/monitoring_api_integration/apis/apm/overview.ts new file mode 100644 index 000000000000..f1c964d5ba89 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/apm/overview.ts @@ -0,0 +1,39 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import response from '../../fixtures/apm/overview.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Overview', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/apm', + getService, + }); + + const timeRange = { + min: '2022-12-20T16:28:00.000Z', + max: '2022-12-20T16:32:00.000Z', + }; + + testRunner(() => { + it('should summarize apm cluster with metrics', async () => { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/3_pOMySBSkCwdyxxBdDbvA/apm') + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(response); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/beats/beat.ts b/x-pack/test/monitoring_api_integration/apis/beats/beat.ts new file mode 100644 index 000000000000..8ed1ab8727f2 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/beats/beat.ts @@ -0,0 +1,42 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import response from '../../fixtures/beats/beat.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Beat', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/beats', + getService, + }); + + const timeRange = { + min: '2022-12-20T17:19:00.000Z', + max: '2022-12-20T17:22:00.000Z', + }; + + testRunner(() => { + it('should load individual beat', async () => { + const { body } = await supertest + .post( + '/api/monitoring/v1/clusters/3_pOMySBSkCwdyxxBdDbvA/beats/beat/52bac4f9-2985-467e-b00c-b3e9ca53fc57' + ) + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(response); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/beats/beats.ts b/x-pack/test/monitoring_api_integration/apis/beats/beats.ts new file mode 100644 index 000000000000..74158ed7e21d --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/beats/beats.ts @@ -0,0 +1,40 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import response from '../../fixtures/beats/beats.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Beats', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/beats', + getService, + }); + + const timeRange = { + min: '2022-12-20T17:19:00.000Z', + max: '2022-12-20T17:22:00.000Z', + }; + + testRunner(() => { + it('should load beats', async () => { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/3_pOMySBSkCwdyxxBdDbvA/beats/beats') + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(body).to.eql(response); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/beats/index.ts b/x-pack/test/monitoring_api_integration/apis/beats/index.ts new file mode 100644 index 000000000000..01d2bc6a3c3d --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/beats/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Beats', () => { + loadTestFile(require.resolve('./overview')); + loadTestFile(require.resolve('./beats')); + loadTestFile(require.resolve('./beat')); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/beats/overview.ts b/x-pack/test/monitoring_api_integration/apis/beats/overview.ts new file mode 100644 index 000000000000..3c8b89a7a749 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/beats/overview.ts @@ -0,0 +1,46 @@ +/* + * 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 { omit } from 'lodash'; +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { getTestRunner } from '../../utils/test_runner'; + +import response from '../../fixtures/beats/overview.json'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const testRunner = getTestRunner({ + testName: 'Overview', + archiveRoot: 'x-pack/test/monitoring_api_integration/archives/beats', + getService, + }); + + const timeRange = { + min: '2022-12-20T17:19:00.000Z', + max: '2022-12-20T17:22:00.000Z', + }; + + // beats overview returns data relative to the current time which bypasses + // any selected period + const omitLatestProperties = (overview: any) => + omit(overview, ['latestActive', 'latestTypes', 'latestVersions']); + + testRunner(() => { + it('should summarize beats cluster with metrics', async () => { + const { body } = await supertest + .post('/api/monitoring/v1/clusters/3_pOMySBSkCwdyxxBdDbvA/beats') + .set('kbn-xsrf', 'xxx') + .send({ timeRange }) + .expect(200); + + expect(omitLatestProperties(body)).to.eql(omitLatestProperties(response)); + }); + }); +} diff --git a/x-pack/test/monitoring_api_integration/apis/index.ts b/x-pack/test/monitoring_api_integration/apis/index.ts new file mode 100644 index 000000000000..0d3099275ada --- /dev/null +++ b/x-pack/test/monitoring_api_integration/apis/index.ts @@ -0,0 +1,15 @@ +/* + * 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 { FtrProviderContext } from '../../api_integration/ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Monitoring Endpoints', function () { + loadTestFile(require.resolve('./apm')); + loadTestFile(require.resolve('./beats')); + }); +} diff --git a/x-pack/test/monitoring_api_integration/archives/apm/metricbeat/data.json.gz b/x-pack/test/monitoring_api_integration/archives/apm/metricbeat/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..da4d8b9529421d817a6fdf813f5abd0762f9d571 GIT binary patch literal 14823 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 literal 0 HcmV?d00001 diff --git a/x-pack/test/monitoring_api_integration/archives/beats/metricbeat/data.json.gz b/x-pack/test/monitoring_api_integration/archives/beats/metricbeat/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..6fff72221f0f3fb24998dc3898841d5a99234181 GIT binary patch literal 8902 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 literal 0 HcmV?d00001 diff --git a/x-pack/test/monitoring_api_integration/archives/beats/package/data.json.gz b/x-pack/test/monitoring_api_integration/archives/beats/package/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..e2b626109383ccd64cd83ee734a68331f567fdcc GIT binary patch literal 7743 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 literal 0 HcmV?d00001 diff --git a/x-pack/test/monitoring_api_integration/config.ts b/x-pack/test/monitoring_api_integration/config.ts new file mode 100644 index 000000000000..cd016afb6dcf --- /dev/null +++ b/x-pack/test/monitoring_api_integration/config.ts @@ -0,0 +1,32 @@ +/* + * 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 { FtrConfigProviderContext } from '@kbn/test'; + +import { bundledPackagesLocation, getPackagesArgs } from './packages'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); + + return { + testFiles: [require.resolve('./apis')], + servers: xPackAPITestsConfig.get('servers'), + services: xPackAPITestsConfig.get('services'), + junit: { + reportName: 'X-Pack Monitoring API Integration Tests', + }, + esTestCluster: xPackAPITestsConfig.get('esTestCluster'), + kbnTestServer: { + ...xPackAPITestsConfig.get('kbnTestServer'), + serverArgs: [ + ...xPackAPITestsConfig.get('kbnTestServer.serverArgs'), + `--xpack.fleet.developer.bundledPackageLocation=${bundledPackagesLocation}`, + ...getPackagesArgs(), + ], + }, + }; +} diff --git a/x-pack/test/monitoring_api_integration/fixtures/apm/instance.json b/x-pack/test/monitoring_api_integration/fixtures/apm/instance.json new file mode 100644 index 000000000000..9de77a2f8af1 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/apm/instance.json @@ -0,0 +1 @@ +{"metrics":{"apm_cpu":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cpu.total.value","metricAgg":"max","label":"Total","title":"CPU Utilization","description":"Percentage of CPU time spent executing (user+kernel mode) for the APM process","units":"%","format":"0.[00]","hasCalculation":true,"isDerivative":true},"data":[[1671553680000,0.8999999999999999],[1671553690000,1.0999999999999999],[1671553700000,0.7000000000000001],[1671553710000,0.7000000000000001],[1671553720000,0.7000000000000001],[1671553730000,0.7000000000000001],[1671553740000,0.8],[1671553750000,0.8],[1671553760000,0.6],[1671553770000,0.6],[1671553780000,0.8],[1671553790000,0.6],[1671553800000,0.7000000000000001],[1671553810000,1],[1671553820000,0.6],[1671553830000,0.6],[1671553840000,0.7000000000000001],[1671553850000,0.5],[1671553860000,0.8],[1671553870000,0.6],[1671553880000,0.8],[1671553890000,0.6],[1671553900000,0.7000000000000001],[1671553910000,0.8]]}],"apm_os_load":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.1","metricAgg":"max","label":"1m","title":"System Load","description":"Load average over the last 1 minute","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,8.14],[1671553690000,8.53],[1671553700000,8.36],[1671553710000,7.46],[1671553720000,7.24],[1671553730000,7.35],[1671553740000,7.64],[1671553750000,7.38],[1671553760000,7.1],[1671553770000,6.71],[1671553780000,7.09],[1671553790000,6.55],[1671553800000,6.39],[1671553810000,6.55],[1671553820000,6.85],[1671553830000,7.68],[1671553840000,7.97],[1671553850000,8.26],[1671553860000,7.84],[1671553870000,7.45],[1671553880000,6.98],[1671553890000,7.37],[1671553900000,7.26],[1671553910000,8.71]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.5","metricAgg":"max","label":"5m","title":"System Load","description":"Load average over the last 5 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,7.29],[1671553690000,7.39],[1671553700000,7.4],[1671553710000,7.24],[1671553720000,7.19],[1671553730000,7.22],[1671553740000,7.28],[1671553750000,7.24],[1671553760000,7.18],[1671553770000,7.09],[1671553780000,7.16],[1671553790000,7.04],[1671553800000,6.98],[1671553810000,7],[1671553820000,7.05],[1671553830000,7.23],[1671553840000,7.3],[1671553850000,7.39],[1671553860000,7.33],[1671553870000,7.27],[1671553880000,7.18],[1671553890000,7.25],[1671553900000,7.23],[1671553910000,7.55]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.15","metricAgg":"max","label":"15m","title":"System Load","description":"Load average over the last 15 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,6.33],[1671553690000,6.38],[1671553700000,6.39],[1671553710000,6.35],[1671553720000,6.35],[1671553730000,6.36],[1671553740000,6.39],[1671553750000,6.39],[1671553760000,6.38],[1671553770000,6.36],[1671553780000,6.39],[1671553790000,6.36],[1671553800000,6.35],[1671553810000,6.36],[1671553820000,6.38],[1671553830000,6.45],[1671553840000,6.48],[1671553850000,6.52],[1671553860000,6.51],[1671553870000,6.5],[1671553880000,6.48],[1671553890000,6.51],[1671553900000,6.51],[1671553910000,6.62]]}],"apm_memory":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.memory_alloc","metricAgg":"max","label":"Allocated Memory","title":"Memory","description":"Allocated memory","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,74667048],[1671553690000,70459976],[1671553700000,77360992],[1671553710000,81251136],[1671553720000,85216456],[1671553730000,88110208],[1671553740000,91557192],[1671553750000,94456664],[1671553760000,97622808],[1671553770000,100815144],[1671553780000,103998280],[1671553790000,106893952],[1671553800000,109766368],[1671553810000,74212112],[1671553820000,77301432],[1671553830000,80857512],[1671553840000,84051760],[1671553850000,87008336],[1671553860000,90740040],[1671553870000,94185272],[1671553880000,97619960],[1671553890000,101186016],[1671553900000,104705344],[1671553910000,108116392]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.rss","metricAgg":"max","label":"Process Total","title":"Memory","description":"Resident set size of memory reserved by the APM service from the OS","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,94416896],[1671553690000,101371904],[1671553700000,104726528],[1671553710000,104726528],[1671553720000,106180608],[1671553730000,109170688],[1671553740000,112689152],[1671553750000,115728384],[1671553760000,119001088],[1671553770000,122474496],[1671553780000,126062592],[1671553790000,128733184],[1671553800000,131887104],[1671553810000,134696960],[1671553820000,134942720],[1671553830000,134942720],[1671553840000,134942720],[1671553850000,134942720],[1671553860000,135168000],[1671553870000,135426048],[1671553880000,135426048],[1671553890000,135426048],[1671553900000,135688192],[1671553910000,135688192]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,94698752],[1671553690000,136135528],[1671553700000,136135528],[1671553710000,136135528],[1671553720000,136135528],[1671553730000,136135528],[1671553740000,136135528],[1671553750000,136135528],[1671553760000,136135528],[1671553770000,136135528],[1671553780000,136135528],[1671553790000,136135528],[1671553800000,136135528],[1671553810000,146053064],[1671553820000,146053064],[1671553830000,146053064],[1671553840000,146053064],[1671553850000,146053064],[1671553860000,146053064],[1671553870000,146053064],[1671553880000,146053064],[1671553890000,146053064],[1671553900000,146053064],[1671553910000,146053064]]}],"apm_memory_cgroup":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.usage.bytes","metricAgg":"max","label":"Memory Utilization (cgroup)","title":"Memory","description":"Memory usage of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,null],[1671553690000,null],[1671553700000,null],[1671553710000,null],[1671553720000,null],[1671553730000,null],[1671553740000,null],[1671553750000,null],[1671553760000,null],[1671553770000,null],[1671553780000,null],[1671553790000,null],[1671553800000,null],[1671553810000,null],[1671553820000,null],[1671553830000,null],[1671553840000,null],[1671553850000,null],[1671553860000,null],[1671553870000,null],[1671553880000,null],[1671553890000,null],[1671553900000,null],[1671553910000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.limit.bytes","metricAgg":"max","label":"Memory Limit","title":"Memory","description":"Memory limit of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,null],[1671553690000,null],[1671553700000,null],[1671553710000,null],[1671553720000,null],[1671553730000,null],[1671553740000,null],[1671553750000,null],[1671553760000,null],[1671553770000,null],[1671553780000,null],[1671553790000,null],[1671553800000,null],[1671553810000,null],[1671553820000,null],[1671553830000,null],[1671553840000,null],[1671553850000,null],[1671553860000,null],[1671553870000,null],[1671553880000,null],[1671553890000,null],[1671553900000,null],[1671553910000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,94698752],[1671553690000,136135528],[1671553700000,136135528],[1671553710000,136135528],[1671553720000,136135528],[1671553730000,136135528],[1671553740000,136135528],[1671553750000,136135528],[1671553760000,136135528],[1671553770000,136135528],[1671553780000,136135528],[1671553790000,136135528],[1671553800000,136135528],[1671553810000,146053064],[1671553820000,146053064],[1671553830000,146053064],[1671553840000,146053064],[1671553850000,146053064],[1671553860000,146053064],[1671553870000,146053064],[1671553880000,146053064],[1671553890000,146053064],[1671553900000,146053064],[1671553910000,146053064]]}],"apm_output_events_rate_success":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.total","metricAgg":"max","label":"Total","title":"Output Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,8.5],[1671553690000,6],[1671553700000,8],[1671553710000,9.6],[1671553720000,9.6],[1671553730000,7.2],[1671553740000,7.7],[1671553750000,7.2],[1671553760000,7.6],[1671553770000,6.8],[1671553780000,7.6],[1671553790000,6.8],[1671553800000,6.9],[1671553810000,8],[1671553820000,7.2],[1671553830000,8.8],[1671553840000,7.2],[1671553850000,6.8],[1671553860000,8.9],[1671553870000,8.4],[1671553880000,8.4],[1671553890000,8.4],[1671553900000,9.2],[1671553910000,8]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.active","metricAgg":"max","label":"Active","title":"Output Active Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0.4],[1671553690000,null],[1671553700000,0.4],[1671553710000,null],[1671553720000,0.8],[1671553730000,null],[1671553740000,0.4],[1671553750000,null],[1671553760000,0.4],[1671553770000,null],[1671553780000,0.8],[1671553790000,null],[1671553800000,0],[1671553810000,0.8],[1671553820000,null],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0.4],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.acked","metricAgg":"max","label":"Acked","title":"Output Acked Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,8.1],[1671553690000,6.4],[1671553700000,7.6],[1671553710000,10],[1671553720000,8.8],[1671553730000,7.6],[1671553740000,7.3],[1671553750000,8],[1671553760000,7.2],[1671553770000,7.2],[1671553780000,6.8],[1671553790000,7.6],[1671553800000,6.9],[1671553810000,7.2],[1671553820000,7.6],[1671553830000,8.8],[1671553840000,7.2],[1671553850000,6.8],[1671553860000,8.5],[1671553870000,8.4],[1671553880000,8.4],[1671553890000,8.4],[1671553900000,9.2],[1671553910000,8.4]]}],"apm_output_events_rate_failure":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.failed","metricAgg":"max","label":"Failed","title":"Output Failed Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.dropped","metricAgg":"max","label":"Dropped","title":"Output Dropped Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]}],"apm_responses_valid":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.count","metricAgg":"max","label":"Total","title":"Response Count Intake API","description":"HTTP Requests responded to by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.ok","metricAgg":"max","label":"Ok","title":"Ok","description":"200 OK response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.accepted","metricAgg":"max","label":"Accepted","title":"Accepted","description":"HTTP Requests successfully reporting new events","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]}],"apm_responses_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.toolarge","metricAgg":"max","label":"Too large","title":"Response Errors Intake API","description":"HTTP Requests rejected due to excessive payload size","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.validate","metricAgg":"max","label":"Validate","title":"Validate","description":"HTTP Requests rejected due to payload validation error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.method","metricAgg":"max","label":"Method","title":"Method","description":"HTTP Requests rejected due to incorrect HTTP method","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.unauthorized","metricAgg":"max","label":"Unauthorized","title":"Unauthorized","description":"HTTP Requests rejected due to invalid secret token","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.ratelimit","metricAgg":"max","label":"Rate limit","title":"Rate limit","description":"HTTP Requests rejected to due excessive rate limit","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.queue","metricAgg":"max","label":"Queue","title":"Queue","description":"HTTP Requests rejected to due internal queue filling up","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.decode","metricAgg":"max","label":"Decode","title":"Decode","description":"HTTP Requests rejected to due decoding errors - invalid json, incorrect data type for entity","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.forbidden","metricAgg":"max","label":"Forbidden","title":"Forbidden","description":"Forbidden HTTP Requests rejected - CORS violation, disabled enpoint","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.concurrency","metricAgg":"max","label":"Concurrency","title":"Concurrency","description":"HTTP Requests rejected due to overall concurrency limit breach","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.closed","metricAgg":"max","label":"Closed","title":"Closed","description":"HTTP Requests rejected during server shutdown","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.internal","metricAgg":"max","label":"Internal","title":"Internal","description":"HTTP Requests rejected due to a miscellaneous internal error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]}],"apm_requests":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.request.count","metricAgg":"max","label":"Requested","title":"Request Count Intake API","description":"HTTP Requests received by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]}],"apm_transformations":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.transaction.transformations","metricAgg":"max","label":"Transaction","title":"Processed Events","description":"Transaction events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.span.transformations","metricAgg":"max","label":"Span","title":"Transformations","description":"Span events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.error.transformations","metricAgg":"max","label":"Error","title":"Transformations","description":"Error events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.metric.transformations","metricAgg":"max","label":"Metric","title":"Transformations","description":"Metric events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.2],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,2],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.8],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.3],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]}],"apm_acm_response":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.count","metricAgg":"max","label":"Count","title":"Response Count Agent Configuration Management","description":"HTTP requests responded to by APM Server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.count","metricAgg":"max","label":"Error Count","title":"Response Error Count Agent Configuration Management","description":"HTTP errors count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.valid.ok","metricAgg":"max","label":"OK","title":"Response OK Count Agent Configuration Management","description":"200 OK response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.valid.notmodified","metricAgg":"max","label":"Not Modified","title":"Response Not Modified Agent Configuration Management","description":"304 Not modified response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]}],"apm_acm_response_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.forbidden","metricAgg":"max","label":"Count","title":"Response Errors Agent Configuration Management","description":"Forbidden HTTP requests rejected count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.unauthorized","metricAgg":"max","label":"Unauthorized","title":"Response Unauthorized Errors Agent Configuration Management","description":"Unauthorized HTTP requests rejected count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.unavailable","metricAgg":"max","label":"Unavailable","title":"Response Unavailable Errors Agent Configuration Management","description":"Unavailable HTTP response count. Possible misconfiguration or unsupported version of Kibana","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.method","metricAgg":"max","label":"Method","title":"Response Method Errors Agent Configuration Management","description":"HTTP requests rejected due to incorrect HTTP method","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.response.errors.invalidquery","metricAgg":"max","label":"Invalid Query","title":"Response Invalid Query Errors Agent Configuration Management","description":"Invalid HTTP query","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]}],"apm_acm_request_count":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.acm.request.count","metricAgg":"max","label":"Count","title":"Requests Agent Configuration Management","description":"HTTP Requests received by agent configuration managemen","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]}]},"apmSummary":{"uuid":"503d57eb-6f1f-4991-89c3-34250cef80d1","transportAddress":"42646a2ef4c2","version":"8.7.0","name":"42646a2ef4c2","type":"Apm-server","output":"Elasticsearch","configReloads":null,"uptime":302835,"eventsTotal":2125,"eventsEmitted":null,"eventsDropped":null,"bytesWritten":797511,"config":{"container":false},"timeOfLastEvent":"2022-12-20T16:32:03.394Z"}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/apm/instances.json b/x-pack/test/monitoring_api_integration/fixtures/apm/instances.json new file mode 100644 index 000000000000..82d67b2c6357 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/apm/instances.json @@ -0,0 +1 @@ +{"stats":{"totalEvents":1803,"apms":{"total":1},"timeOfLastEvent":"2022-12-20T16:31:53.427Z"},"apms":[{"uuid":"503d57eb-6f1f-4991-89c3-34250cef80d1","name":"42646a2ef4c2","type":"Apm-server","output":"Elasticsearch","total_events_rate":7.5125,"bytes_sent_rate":2805.175,"errors":0,"memory":108116392,"version":"8.7.0","time_of_last_event":"2022-12-20T16:31:53.427Z"}],"cgroup":false} diff --git a/x-pack/test/monitoring_api_integration/fixtures/apm/overview.json b/x-pack/test/monitoring_api_integration/fixtures/apm/overview.json new file mode 100644 index 000000000000..e2f6c6cb65cc --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/apm/overview.json @@ -0,0 +1 @@ +{"stats":{"totalEvents":1803,"memRss":41271296,"apms":{"total":1},"versions":["8.7.0"],"timeOfLastEvent":"2022-12-20T16:31:53.427Z","config":{"container":false}},"metrics":{"apm_cpu":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cpu.total.value","metricAgg":"max","label":"Total","title":"CPU Utilization","description":"Percentage of CPU time spent executing (user+kernel mode) for the APM process","units":"%","format":"0.[00]","hasCalculation":true,"isDerivative":true},"data":[[1671553680000,0.8999999999999999],[1671553690000,1.0999999999999999],[1671553700000,0.7000000000000001],[1671553710000,0.7000000000000001],[1671553720000,0.7000000000000001],[1671553730000,0.7000000000000001],[1671553740000,0.8],[1671553750000,0.8],[1671553760000,0.6],[1671553770000,0.6],[1671553780000,0.8],[1671553790000,0.6],[1671553800000,0.7000000000000001],[1671553810000,1],[1671553820000,0.6],[1671553830000,0.6],[1671553840000,0.7000000000000001],[1671553850000,0.5],[1671553860000,0.8],[1671553870000,0.6],[1671553880000,0.8],[1671553890000,0.6],[1671553900000,0.7000000000000001],[1671553910000,0.8]]}],"apm_os_load":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.1","metricAgg":"max","label":"1m","title":"System Load","description":"Load average over the last 1 minute","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,8.14],[1671553690000,8.53],[1671553700000,8.36],[1671553710000,7.46],[1671553720000,7.24],[1671553730000,7.35],[1671553740000,7.64],[1671553750000,7.38],[1671553760000,7.1],[1671553770000,6.71],[1671553780000,7.09],[1671553790000,6.55],[1671553800000,6.39],[1671553810000,6.55],[1671553820000,6.85],[1671553830000,7.68],[1671553840000,7.97],[1671553850000,8.26],[1671553860000,7.84],[1671553870000,7.45],[1671553880000,6.98],[1671553890000,7.37],[1671553900000,7.26],[1671553910000,8.71]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.5","metricAgg":"max","label":"5m","title":"System Load","description":"Load average over the last 5 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,7.29],[1671553690000,7.39],[1671553700000,7.4],[1671553710000,7.24],[1671553720000,7.19],[1671553730000,7.22],[1671553740000,7.28],[1671553750000,7.24],[1671553760000,7.18],[1671553770000,7.09],[1671553780000,7.16],[1671553790000,7.04],[1671553800000,6.98],[1671553810000,7],[1671553820000,7.05],[1671553830000,7.23],[1671553840000,7.3],[1671553850000,7.39],[1671553860000,7.33],[1671553870000,7.27],[1671553880000,7.18],[1671553890000,7.25],[1671553900000,7.23],[1671553910000,7.55]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.system.load.15","metricAgg":"max","label":"15m","title":"System Load","description":"Load average over the last 15 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,6.33],[1671553690000,6.38],[1671553700000,6.39],[1671553710000,6.35],[1671553720000,6.35],[1671553730000,6.36],[1671553740000,6.39],[1671553750000,6.39],[1671553760000,6.38],[1671553770000,6.36],[1671553780000,6.39],[1671553790000,6.36],[1671553800000,6.35],[1671553810000,6.36],[1671553820000,6.38],[1671553830000,6.45],[1671553840000,6.48],[1671553850000,6.52],[1671553860000,6.51],[1671553870000,6.5],[1671553880000,6.48],[1671553890000,6.51],[1671553900000,6.51],[1671553910000,6.62]]}],"apm_memory":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.memory_alloc","metricAgg":"max","label":"Allocated Memory","title":"Memory","description":"Allocated memory","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,74667048],[1671553690000,70459976],[1671553700000,77360992],[1671553710000,81251136],[1671553720000,85216456],[1671553730000,88110208],[1671553740000,91557192],[1671553750000,94456664],[1671553760000,97622808],[1671553770000,100815144],[1671553780000,103998280],[1671553790000,106893952],[1671553800000,109766368],[1671553810000,74212112],[1671553820000,77301432],[1671553830000,80857512],[1671553840000,84051760],[1671553850000,87008336],[1671553860000,90740040],[1671553870000,94185272],[1671553880000,97619960],[1671553890000,101186016],[1671553900000,104705344],[1671553910000,108116392]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.rss","metricAgg":"max","label":"Process Total","title":"Memory","description":"Resident set size of memory reserved by the APM service from the OS","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,94416896],[1671553690000,101371904],[1671553700000,104726528],[1671553710000,104726528],[1671553720000,106180608],[1671553730000,109170688],[1671553740000,112689152],[1671553750000,115728384],[1671553760000,119001088],[1671553770000,122474496],[1671553780000,126062592],[1671553790000,128733184],[1671553800000,131887104],[1671553810000,134696960],[1671553820000,134942720],[1671553830000,134942720],[1671553840000,134942720],[1671553850000,134942720],[1671553860000,135168000],[1671553870000,135426048],[1671553880000,135426048],[1671553890000,135426048],[1671553900000,135688192],[1671553910000,135688192]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,94698752],[1671553690000,136135528],[1671553700000,136135528],[1671553710000,136135528],[1671553720000,136135528],[1671553730000,136135528],[1671553740000,136135528],[1671553750000,136135528],[1671553760000,136135528],[1671553770000,136135528],[1671553780000,136135528],[1671553790000,136135528],[1671553800000,136135528],[1671553810000,146053064],[1671553820000,146053064],[1671553830000,146053064],[1671553840000,146053064],[1671553850000,146053064],[1671553860000,146053064],[1671553870000,146053064],[1671553880000,146053064],[1671553890000,146053064],[1671553900000,146053064],[1671553910000,146053064]]}],"apm_memory_cgroup":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.usage.bytes","metricAgg":"max","label":"Memory Utilization (cgroup)","title":"Memory","description":"Memory usage of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,null],[1671553690000,null],[1671553700000,null],[1671553710000,null],[1671553720000,null],[1671553730000,null],[1671553740000,null],[1671553750000,null],[1671553760000,null],[1671553770000,null],[1671553780000,null],[1671553790000,null],[1671553800000,null],[1671553810000,null],[1671553820000,null],[1671553830000,null],[1671553840000,null],[1671553850000,null],[1671553860000,null],[1671553870000,null],[1671553880000,null],[1671553890000,null],[1671553900000,null],[1671553910000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.cgroup.memory.mem.limit.bytes","metricAgg":"max","label":"Memory Limit","title":"Memory","description":"Memory limit of the container","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,null],[1671553690000,null],[1671553700000,null],[1671553710000,null],[1671553720000,null],[1671553730000,null],[1671553740000,null],[1671553750000,null],[1671553760000,null],[1671553770000,null],[1671553780000,null],[1671553790000,null],[1671553800000,null],[1671553810000,null],[1671553820000,null],[1671553830000,null],[1671553840000,null],[1671553850000,null],[1671553860000,null],[1671553870000,null],[1671553880000,null],[1671553890000,null],[1671553900000,null],[1671553910000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671553680000,94698752],[1671553690000,136135528],[1671553700000,136135528],[1671553710000,136135528],[1671553720000,136135528],[1671553730000,136135528],[1671553740000,136135528],[1671553750000,136135528],[1671553760000,136135528],[1671553770000,136135528],[1671553780000,136135528],[1671553790000,136135528],[1671553800000,136135528],[1671553810000,146053064],[1671553820000,146053064],[1671553830000,146053064],[1671553840000,146053064],[1671553850000,146053064],[1671553860000,146053064],[1671553870000,146053064],[1671553880000,146053064],[1671553890000,146053064],[1671553900000,146053064],[1671553910000,146053064]]}],"apm_output_events_rate_success":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.total","metricAgg":"max","label":"Total","title":"Output Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,8.5],[1671553690000,6],[1671553700000,8],[1671553710000,9.6],[1671553720000,9.6],[1671553730000,7.2],[1671553740000,7.7],[1671553750000,7.2],[1671553760000,7.6],[1671553770000,6.8],[1671553780000,7.6],[1671553790000,6.8],[1671553800000,6.9],[1671553810000,8],[1671553820000,7.2],[1671553830000,8.8],[1671553840000,7.2],[1671553850000,6.8],[1671553860000,8.9],[1671553870000,8.4],[1671553880000,8.4],[1671553890000,8.4],[1671553900000,9.2],[1671553910000,8]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.active","metricAgg":"max","label":"Active","title":"Output Active Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0.4],[1671553690000,null],[1671553700000,0.4],[1671553710000,null],[1671553720000,0.8],[1671553730000,null],[1671553740000,0.4],[1671553750000,null],[1671553760000,0.4],[1671553770000,null],[1671553780000,0.8],[1671553790000,null],[1671553800000,0],[1671553810000,0.8],[1671553820000,null],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0.4],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,null]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.acked","metricAgg":"max","label":"Acked","title":"Output Acked Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,8.1],[1671553690000,6.4],[1671553700000,7.6],[1671553710000,10],[1671553720000,8.8],[1671553730000,7.6],[1671553740000,7.3],[1671553750000,8],[1671553760000,7.2],[1671553770000,7.2],[1671553780000,6.8],[1671553790000,7.6],[1671553800000,6.9],[1671553810000,7.2],[1671553820000,7.6],[1671553830000,8.8],[1671553840000,7.2],[1671553850000,6.8],[1671553860000,8.5],[1671553870000,8.4],[1671553880000,8.4],[1671553890000,8.4],[1671553900000,9.2],[1671553910000,8.4]]}],"apm_output_events_rate_failure":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.failed","metricAgg":"max","label":"Failed","title":"Output Failed Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.libbeat.output.events.dropped","metricAgg":"max","label":"Dropped","title":"Output Dropped Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]}],"apm_responses_valid":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.count","metricAgg":"max","label":"Total","title":"Response Count Intake API","description":"HTTP Requests responded to by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.ok","metricAgg":"max","label":"Ok","title":"Ok","description":"200 OK response count","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.valid.accepted","metricAgg":"max","label":"Accepted","title":"Accepted","description":"HTTP Requests successfully reporting new events","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]}],"apm_responses_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.toolarge","metricAgg":"max","label":"Too large","title":"Response Errors Intake API","description":"HTTP Requests rejected due to excessive payload size","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.validate","metricAgg":"max","label":"Validate","title":"Validate","description":"HTTP Requests rejected due to payload validation error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.method","metricAgg":"max","label":"Method","title":"Method","description":"HTTP Requests rejected due to incorrect HTTP method","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.unauthorized","metricAgg":"max","label":"Unauthorized","title":"Unauthorized","description":"HTTP Requests rejected due to invalid secret token","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.ratelimit","metricAgg":"max","label":"Rate limit","title":"Rate limit","description":"HTTP Requests rejected to due excessive rate limit","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.queue","metricAgg":"max","label":"Queue","title":"Queue","description":"HTTP Requests rejected to due internal queue filling up","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.decode","metricAgg":"max","label":"Decode","title":"Decode","description":"HTTP Requests rejected to due decoding errors - invalid json, incorrect data type for entity","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.forbidden","metricAgg":"max","label":"Forbidden","title":"Forbidden","description":"Forbidden HTTP Requests rejected - CORS violation, disabled enpoint","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.concurrency","metricAgg":"max","label":"Concurrency","title":"Concurrency","description":"HTTP Requests rejected due to overall concurrency limit breach","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.closed","metricAgg":"max","label":"Closed","title":"Closed","description":"HTTP Requests rejected during server shutdown","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.response.errors.internal","metricAgg":"max","label":"Internal","title":"Internal","description":"HTTP Requests rejected due to a miscellaneous internal error","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,0],[1671553690000,0],[1671553700000,0],[1671553710000,0],[1671553720000,0],[1671553730000,0],[1671553740000,0],[1671553750000,0],[1671553760000,0],[1671553770000,0],[1671553780000,0],[1671553790000,0],[1671553800000,0],[1671553810000,0],[1671553820000,0],[1671553830000,0],[1671553840000,0],[1671553850000,0],[1671553860000,0],[1671553870000,0],[1671553880000,0],[1671553890000,0],[1671553900000,0],[1671553910000,0]]}],"apm_requests":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.server.request.count","metricAgg":"max","label":"Requested","title":"Request Count Intake API","description":"HTTP Requests received by server","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]}],"apm_transformations":[{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.transaction.transformations","metricAgg":"max","label":"Transaction","title":"Processed Events","description":"Transaction events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.span.transformations","metricAgg":"max","label":"Span","title":"Transformations","description":"Span events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.error.transformations","metricAgg":"max","label":"Error","title":"Transformations","description":"Error events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.1],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,1.9],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.7],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.2],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]},{"bucket_size":"10 seconds","timeRange":{"min":1671553680000,"max":1671553920000},"metric":{"app":"apm","field":"beats_stats.metrics.apm-server.processor.metric.transformations","metricAgg":"max","label":"Metric","title":"Transformations","description":"Metric events processed","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671553680000,2.2],[1671553690000,1.5],[1671553700000,2],[1671553710000,2.4],[1671553720000,2.3],[1671553730000,1.9],[1671553740000,2],[1671553750000,1.8],[1671553760000,1.9],[1671553770000,1.7],[1671553780000,1.9],[1671553790000,1.7],[1671553800000,1.8],[1671553810000,2],[1671553820000,1.8],[1671553830000,2.2],[1671553840000,1.8],[1671553850000,1.7],[1671553860000,2.3],[1671553870000,2.1],[1671553880000,2.1],[1671553890000,2.1],[1671553900000,2.3],[1671553910000,2]]}]}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/beats/beat.json b/x-pack/test/monitoring_api_integration/fixtures/beats/beat.json new file mode 100644 index 000000000000..fa44e27906dd --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/beats/beat.json @@ -0,0 +1 @@ +{"summary":{"uuid":"52bac4f9-2985-467e-b00c-b3e9ca53fc57","transportAddress":"768578f6a263","version":"8.7.0","name":"768578f6a263","type":"Metricbeat","output":"Elasticsearch","configReloads":0,"uptime":240791,"eventsTotal":238,"eventsEmitted":238,"eventsDropped":0,"bytesWritten":249724,"handlesHardLimit":1048576,"handlesSoftLimit":1048576},"metrics":{"beat_event_rates":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.total","metricAgg":"max","label":"New","title":"Events Rate","description":"New events sent to the publishing pipeline","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.4],[1671556760000,1.4],[1671556770000,1.4],[1671556780000,1.4],[1671556790000,1.4],[1671556800000,1.4],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.4],[1671556870000,1.4],[1671556880000,1.4],[1671556890000,1.4],[1671556900000,1.4],[1671556910000,1.4]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.events.total","metricAgg":"max","label":"Emitted","title":"Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.4],[1671556760000,1.4],[1671556770000,1.4],[1671556780000,1.4],[1671556790000,1.1],[1671556800000,1.7],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.4],[1671556870000,1.4],[1671556880000,1.4],[1671556890000,1.4],[1671556900000,1.4],[1671556910000,1.4]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.events.acked","metricAgg":"max","label":"Acknowledged","title":"Events Rate","description":"Events acknowledged by the output (includes events dropped by the output)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.7],[1671556760000,1.1],[1671556770000,1.7],[1671556780000,1.1],[1671556790000,1.4],[1671556800000,1.7],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.1],[1671556870000,1.7],[1671556880000,1.1],[1671556890000,1.7],[1671556900000,1.4],[1671556910000,1.4]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.published","metricAgg":"max","label":"Queued","title":"Events Rate","description":"Events added to the event pipeline queue","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.4],[1671556760000,1.4],[1671556770000,1.4],[1671556780000,1.4],[1671556790000,1.4],[1671556800000,1.4],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.4],[1671556870000,1.4],[1671556880000,1.4],[1671556890000,1.4],[1671556900000,1.4],[1671556910000,1.4]]}],"beat_fail_rates":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.failed","metricAgg":"max","label":"Failed in Pipeline","title":"Fail Rates","description":"Failures that happened before event was added to the publishing pipeline (output was disabled or publisher client closed)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.dropped","metricAgg":"max","label":"Dropped in Pipeline","title":"Fail Rates","description":"Events that have been dropped after N retries (N = max_retries setting)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.events.dropped","metricAgg":"max","label":"Dropped in Output","title":"Fail Rates","description":"(Fatal drop) Events dropped by the output as being \"invalid.\" The output still acknowledges the event for the Beat to remove it from the queue.","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.retry","metricAgg":"max","label":"Retry in Pipeline","title":"Fail Rates","description":"Events in the pipeline that are trying again to be sent to the output","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]}],"beat_throughput_rates":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.write.bytes","metricAgg":"max","label":"Bytes Sent","title":"Throughput","description":"Bytes written to the output (consists of size of network headers and compressed payload)","units":"/s","format":"0,0.0 b","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1469.4],[1671556750000,1468.1],[1671556760000,1468.4],[1671556770000,1468.6],[1671556780000,1468.5],[1671556790000,814.6],[1671556800000,2123.1],[1671556810000,1467.4],[1671556820000,1469.3],[1671556830000,1469.3],[1671556840000,1470.1],[1671556850000,1468.7],[1671556860000,1469.1],[1671556870000,1469.6],[1671556880000,1468.6],[1671556890000,1469.5],[1671556900000,1469.7],[1671556910000,1469.8]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.read.bytes","metricAgg":"max","label":"Bytes Received","title":"Throughput","description":"Bytes read in response from the output","units":"/s","format":"0,0.0 b","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,360.3],[1671556750000,439.5],[1671556760000,281.1],[1671556770000,439.5],[1671556780000,281.1],[1671556790000,360.3],[1671556800000,439.5],[1671556810000,360.3],[1671556820000,360.3],[1671556830000,360.3],[1671556840000,360.3],[1671556850000,360.3],[1671556860000,281.1],[1671556870000,439.6],[1671556880000,281.1],[1671556890000,439.5],[1671556900000,360.3],[1671556910000,360.3]]}],"beat_output_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.write.errors","metricAgg":"max","label":"Sending","title":"Output Errors","description":"Errors in writing the response from the output","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.read.errors","metricAgg":"max","label":"Receiving","title":"Output Errors","description":"Errors in reading the response from the output","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]}],"beat_memory":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.beat.memstats.memory_alloc","metricAgg":"max","label":"Active","title":"Memory","description":"Private memory in active use by the Beat","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671556740000,18599768],[1671556750000,20315696],[1671556760000,12195672],[1671556770000,13704704],[1671556780000,15738384],[1671556790000,17622208],[1671556800000,19388904],[1671556810000,20921304],[1671556820000,13330344],[1671556830000,15049616],[1671556840000,16696576],[1671556850000,18663480],[1671556860000,20330816],[1671556870000,12115408],[1671556880000,13902128],[1671556890000,15626304],[1671556900000,17293064],[1671556910000,12066528]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.beat.memstats.rss","metricAgg":"max","label":"Process Total","title":"Memory","description":"Resident set size of memory reserved by the Beat from the OS","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671556740000,147853312],[1671556750000,147853312],[1671556760000,149712896],[1671556770000,150511616],[1671556780000,150511616],[1671556790000,150511616],[1671556800000,150511616],[1671556810000,150511616],[1671556820000,150511616],[1671556830000,150511616],[1671556840000,150511616],[1671556850000,150511616],[1671556860000,150511616],[1671556870000,149843968],[1671556880000,149843968],[1671556890000,149843968],[1671556900000,149843968],[1671556910000,149491712]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.beat.memstats.gc_next","metricAgg":"max","label":"GC Next","title":"Memory","description":"Limit of allocated memory at which garbage collection will occur","units":"B","format":"0,0.0 b","hasCalculation":false,"isDerivative":false},"data":[[1671556740000,22271640],[1671556750000,22271640],[1671556760000,23614600],[1671556770000,23614600],[1671556780000,23614600],[1671556790000,23614600],[1671556800000,23614600],[1671556810000,23614600],[1671556820000,23422216],[1671556830000,23422216],[1671556840000,23422216],[1671556850000,23422216],[1671556860000,23422216],[1671556870000,22842328],[1671556880000,22842328],[1671556890000,22842328],[1671556900000,22842328],[1671556910000,23341576]]}],"beat_cpu_utilization":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.beat.cpu.total.value","metricAgg":"max","label":"Total","title":"CPU Utilization","description":"Percentage of CPU time spent executing (user+kernel mode) for the Beat process","units":"%","format":"0.[00]","hasCalculation":true,"isDerivative":true},"data":[[1671556740000,0.4],[1671556750000,0.4],[1671556760000,0.6],[1671556770000,0.4],[1671556780000,0.4],[1671556790000,0.4],[1671556800000,0.4],[1671556810000,0.5],[1671556820000,0.5],[1671556830000,0.4],[1671556840000,0.5],[1671556850000,0.2],[1671556860000,0.3],[1671556870000,0.7000000000000001],[1671556880000,0.5],[1671556890000,0.5],[1671556900000,0.3],[1671556910000,0.7000000000000001]]}],"beat_os_load":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.system.load.1","metricAgg":"max","label":"1m","title":"System Load","description":"Load average over the last 1 minute","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671556740000,9.66],[1671556750000,9.32],[1671556760000,9.05],[1671556770000,8.2],[1671556780000,7.79],[1671556790000,8.25],[1671556800000,8.54],[1671556810000,8.13],[1671556820000,8.83],[1671556830000,8.71],[1671556840000,8.43],[1671556850000,8],[1671556860000,7.77],[1671556870000,8.19],[1671556880000,7.93],[1671556890000,8.24],[1671556900000,8.4],[1671556910000,8.11]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.system.load.5","metricAgg":"max","label":"5m","title":"System Load","description":"Load average over the last 5 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671556740000,8.24],[1671556750000,8.21],[1671556760000,8.19],[1671556770000,8.03],[1671556780000,7.95],[1671556790000,8.05],[1671556800000,8.11],[1671556810000,8.04],[1671556820000,8.19],[1671556830000,8.19],[1671556840000,8.15],[1671556850000,8.06],[1671556860000,8.01],[1671556870000,8.09],[1671556880000,8.04],[1671556890000,8.1],[1671556900000,8.15],[1671556910000,8.09]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.system.load.15","metricAgg":"max","label":"15m","title":"System Load","description":"Load average over the last 15 minutes","units":"","format":"0,0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671556740000,7.66],[1671556750000,7.66],[1671556760000,7.65],[1671556770000,7.61],[1671556780000,7.59],[1671556790000,7.62],[1671556800000,7.65],[1671556810000,7.63],[1671556820000,7.68],[1671556830000,7.69],[1671556840000,7.68],[1671556850000,7.66],[1671556860000,7.64],[1671556870000,7.67],[1671556880000,7.66],[1671556890000,7.69],[1671556900000,7.71],[1671556910000,7.69]]}],"beat_handles":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.beat.handles.open","metricAgg":"max","label":"Open Handles","title":"Open Handles","description":"Count of open file handlers","units":"","format":"0.[00]","hasCalculation":false,"isDerivative":false},"data":[[1671556740000,18],[1671556750000,19],[1671556760000,13],[1671556770000,14],[1671556780000,15],[1671556790000,16],[1671556800000,17],[1671556810000,13],[1671556820000,14],[1671556830000,15],[1671556840000,16],[1671556850000,17],[1671556860000,18],[1671556870000,13],[1671556880000,14],[1671556890000,15],[1671556900000,16],[1671556910000,13]]}]}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/beats/beats.json b/x-pack/test/monitoring_api_integration/fixtures/beats/beats.json new file mode 100644 index 000000000000..df8a6519b34b --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/beats/beats.json @@ -0,0 +1 @@ +{"stats":{"total":1,"types":[{"type":"Metricbeat","count":1}],"stats":{"totalEvents":238,"bytesSent":249724}},"listing":[{"uuid":"52bac4f9-2985-467e-b00c-b3e9ca53fc57","name":"768578f6a263","type":"Metricbeat","output":"Elasticsearch","total_events_rate":1.3222222222222222,"bytes_sent_rate":1387.3555555555556,"errors":0,"memory":12066528,"version":"8.7.0"}]} diff --git a/x-pack/test/monitoring_api_integration/fixtures/beats/overview.json b/x-pack/test/monitoring_api_integration/fixtures/beats/overview.json new file mode 100644 index 000000000000..1fa783ef043a --- /dev/null +++ b/x-pack/test/monitoring_api_integration/fixtures/beats/overview.json @@ -0,0 +1 @@ +{"latestActive":[{"range":"last1m","count":1},{"range":"last5m","count":1},{"range":"last20m","count":1},{"range":"last1h","count":1},{"range":"last1d","count":1}],"latestVersions":[{"version":"8.7.0","count":1}],"latestTypes":[{"type":"Metricbeat","count":1}],"stats":{"total":1,"types":[{"type":"Metricbeat","count":1}],"stats":{"totalEvents":238,"bytesSent":249724}},"metrics":{"beat_event_rates":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.total","metricAgg":"max","label":"Total","title":"Events Rate","description":"All events newly created in the publishing pipeline","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.4],[1671556760000,1.4],[1671556770000,1.4],[1671556780000,1.4],[1671556790000,1.4],[1671556800000,1.4],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.4],[1671556870000,1.4],[1671556880000,1.4],[1671556890000,1.4],[1671556900000,1.4],[1671556910000,1.4]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.events.total","metricAgg":"max","label":"Emitted","title":"Events Rate","description":"Events processed by the output (including retries)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.4],[1671556760000,1.4],[1671556770000,1.4],[1671556780000,1.4],[1671556790000,1.1],[1671556800000,1.7],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.4],[1671556870000,1.4],[1671556880000,1.4],[1671556890000,1.4],[1671556900000,1.4],[1671556910000,1.4]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.events.acked","metricAgg":"max","label":"Acknowledged","title":"Events Rate","description":"Events acknowledged by the output (includes events dropped by the output)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.7],[1671556760000,1.1],[1671556770000,1.7],[1671556780000,1.1],[1671556790000,1.4],[1671556800000,1.7],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.1],[1671556870000,1.7],[1671556880000,1.1],[1671556890000,1.7],[1671556900000,1.4],[1671556910000,1.4]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.published","metricAgg":"max","label":"Queued","title":"Events Rate","description":"Events added to the event pipeline queue","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1.4],[1671556750000,1.4],[1671556760000,1.4],[1671556770000,1.4],[1671556780000,1.4],[1671556790000,1.4],[1671556800000,1.4],[1671556810000,1.4],[1671556820000,1.4],[1671556830000,1.4],[1671556840000,1.4],[1671556850000,1.4],[1671556860000,1.4],[1671556870000,1.4],[1671556880000,1.4],[1671556890000,1.4],[1671556900000,1.4],[1671556910000,1.4]]}],"beat_fail_rates":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.failed","metricAgg":"max","label":"Failed in Pipeline","title":"Fail Rates","description":"Failures that happened before event was added to the publishing pipeline (output was disabled or publisher client closed)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.dropped","metricAgg":"max","label":"Dropped in Pipeline","title":"Fail Rates","description":"Events that have been dropped after N retries (N = max_retries setting)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.events.dropped","metricAgg":"max","label":"Dropped in Output","title":"Fail Rates","description":"(Fatal drop) Events dropped by the output as being \"invalid.\" The output still acknowledges the event for the Beat to remove it from the queue.","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.pipeline.events.retry","metricAgg":"max","label":"Retry in Pipeline","title":"Fail Rates","description":"Events in the pipeline that are trying again to be sent to the output","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]}],"beat_throughput_rates":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.write.bytes","metricAgg":"max","label":"Bytes Sent","title":"Throughput","description":"Bytes written to the output (consists of size of network headers and compressed payload)","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,1469.4],[1671556750000,1468.1],[1671556760000,1468.4],[1671556770000,1468.6],[1671556780000,1468.5],[1671556790000,814.6],[1671556800000,2123.1],[1671556810000,1467.4],[1671556820000,1469.3],[1671556830000,1469.3],[1671556840000,1470.1],[1671556850000,1468.7],[1671556860000,1469.1],[1671556870000,1469.6],[1671556880000,1468.6],[1671556890000,1469.5],[1671556900000,1469.7],[1671556910000,1469.8]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.read.bytes","metricAgg":"max","label":"Bytes Received","title":"Throughput","description":"Bytes read in response from the output","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,360.3],[1671556750000,439.5],[1671556760000,281.1],[1671556770000,439.5],[1671556780000,281.1],[1671556790000,360.3],[1671556800000,439.5],[1671556810000,360.3],[1671556820000,360.3],[1671556830000,360.3],[1671556840000,360.3],[1671556850000,360.3],[1671556860000,281.1],[1671556870000,439.6],[1671556880000,281.1],[1671556890000,439.5],[1671556900000,360.3],[1671556910000,360.3]]}],"beat_output_errors":[{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.write.errors","metricAgg":"max","label":"Sending","title":"Output Errors","description":"Errors in writing the response from the output","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]},{"bucket_size":"10 seconds","timeRange":{"min":1671556740000,"max":1671556920000},"metric":{"app":"beats","field":"beats_stats.metrics.libbeat.output.read.errors","metricAgg":"max","label":"Receiving","title":"Output Errors","description":"Errors in reading the response from the output","units":"/s","format":"0,0.[00]","hasCalculation":false,"isDerivative":true},"data":[[1671556740000,0],[1671556750000,0],[1671556760000,0],[1671556770000,0],[1671556780000,0],[1671556790000,0],[1671556800000,0],[1671556810000,0],[1671556820000,0],[1671556830000,0],[1671556840000,0],[1671556850000,0],[1671556860000,0],[1671556870000,0],[1671556880000,0],[1671556890000,0],[1671556900000,0],[1671556910000,0]]}]}} diff --git a/x-pack/test/monitoring_api_integration/fixtures/packages/beat-0.0.1.zip b/x-pack/test/monitoring_api_integration/fixtures/packages/beat-0.0.1.zip new file mode 100644 index 0000000000000000000000000000000000000000..940f7c4d8e96ec261d6de5443e48ca6201d3eafa GIT binary patch literal 121912 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¥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 G^Zx)eVTm08 literal 0 HcmV?d00001 diff --git a/x-pack/test/monitoring_api_integration/packages.ts b/x-pack/test/monitoring_api_integration/packages.ts new file mode 100644 index 000000000000..9c85b89fd105 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/packages.ts @@ -0,0 +1,21 @@ +/* + * 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 path from 'path'; + +const PACKAGES = [{ name: 'beat', version: '0.0.1' }]; + +export const getPackagesArgs = (): string[] => { + return PACKAGES.flatMap((pkg, i) => { + return [ + `--xpack.fleet.packages.${i}.name=${pkg.name}`, + `--xpack.fleet.packages.${i}.version=${pkg.version}`, + ]; + }); +}; + +export const bundledPackagesLocation = path.join(path.dirname(__filename), '/fixtures/packages'); diff --git a/x-pack/test/monitoring_api_integration/scripts/transform_archive.js b/x-pack/test/monitoring_api_integration/scripts/transform_archive.js new file mode 100644 index 000000000000..7f0e4e148624 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/scripts/transform_archive.js @@ -0,0 +1,87 @@ +/* + * 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. + */ + +require('../../../../src/setup_node_env'); + +const { createReadStream, createWriteStream } = require('fs'); +const { mkdir, rm } = require('fs/promises'); +const path = require('path'); +const { Transform } = require('stream'); +const { createGzip } = require('zlib'); +const { createPromiseFromStreams } = require('@kbn/utils'); + +const { + createParseArchiveStreams, +} = require('../../../../packages/kbn-es-archiver/src/lib/archives/parse'); // eslint-disable-line @kbn/imports/uniform_imports + +/** + * generates .monitoring-* (metricbeat) archive from a metrics-* (package) archive + * options: + * --src - eg /path/to/archive/data.json.gz + */ +(async () => { + const opts = process.argv.slice(2); + const sourceFile = opts[opts.findIndex((opt) => opt === '--src') + 1]; + if (!sourceFile) { + throw new Error('missing required --src argument'); + } + + const sourceDir = path.dirname(path.resolve(sourceFile)); + const targetDir = path.join(path.dirname(sourceDir), 'metricbeat'); + const targetFile = path.join(targetDir, 'data.json.gz'); + + console.info(`creating dir ${targetDir}`); + await mkdir(targetDir, { recursive: true }); + + await createPromiseFromStreams([ + createReadStream(sourceFile), + ...createParseArchiveStreams({ gzip: true }), + new Transform({ + objectMode: true, + transform(item, _encoding, callback) { + delete item.value.index; + delete item.value.source.data_stream; + delete item.value.source.elastic_agent; + + const source = item.value.source; + + /* eslint-disable no-nested-ternary */ + const product = source.logstash + ? 'logstash' + : source.kibana + ? 'kibana' + : source.beat + ? 'beats' + : source.elasticsearch + ? 'es' + : null; + /* eslint-enable no-nested-ternary */ + + if (!product) { + console.warn('could not detect source product of document:', source); + return callback(); + } + + item.value.data_stream = `.monitoring-${product}-8-mb`; + callback(null, JSON.stringify(item, null, 2) + '\n'.repeat(2)); + }, + }), + createGzip(), + createWriteStream(targetFile), + ]); + + console.info(`created metricbeat data at ${targetFile}`); + + const mappingsFile = path.join(sourceDir, 'mappings.json'); + try { + await rm(mappingsFile); + console.info(`removed mappings at path ${mappingsFile}`); + } catch (err) { + if (err.code === 'ENOENT') return; + console.warn(`failed to remove mappings at path ${mappingsFile}:`, err); + } +})(); diff --git a/x-pack/test/monitoring_api_integration/utils/lifecycle_methods.ts b/x-pack/test/monitoring_api_integration/utils/lifecycle_methods.ts new file mode 100644 index 000000000000..bd7b86bfc69b --- /dev/null +++ b/x-pack/test/monitoring_api_integration/utils/lifecycle_methods.ts @@ -0,0 +1,39 @@ +/* + * 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 { FtrProviderContext } from '../../api_integration/ftr_provider_context'; + +export const getLifecycleMethods = (getService: FtrProviderContext['getService']) => { + const esArchiver = getService('esArchiver'); + const client = getService('es'); + + const deleteDataStream = async (index: string) => { + await client.transport.request( + { + method: 'DELETE', + path: `_data_stream/${index}`, + }, + { + ignore: [404], + } + ); + }; + + return { + async setup(archives: string[] | string) { + const archivesArray = Array.isArray(archives) ? archives : [archives]; + await Promise.all(archivesArray.map((archive) => esArchiver.load(archive))); + }, + + async tearDown() { + // .monitoring-* and metrics-* mappings are already installed when we initiate + // the tests suites. since the archiver doesn't have any reference to the + // mappings it can't automatically delete it and we have to do the cleanup manually + await deleteDataStream('.monitoring-*'); + await deleteDataStream('metrics-beats.stack_monitoring.*'); + }, + }; +}; diff --git a/x-pack/test/monitoring_api_integration/utils/test_runner.ts b/x-pack/test/monitoring_api_integration/utils/test_runner.ts new file mode 100644 index 000000000000..8f96b8cf1027 --- /dev/null +++ b/x-pack/test/monitoring_api_integration/utils/test_runner.ts @@ -0,0 +1,41 @@ +/* + * 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 { FtrProviderContext } from '../../api_integration/ftr_provider_context'; +import { getLifecycleMethods } from './lifecycle_methods'; + +interface RunTestsOpts { + testName: string; + archiveRoot: string; + getService: FtrProviderContext['getService']; +} + +/** + * abstracts the duplication of tests running once for metricbeat data and once + * for package data. Expects that the provided path defines two + * subdirectories (metricbeat and package) containing their respective archived + * data. The runner takes care of loading and unloading the test data. + */ +export function getTestRunner(opts: RunTestsOpts) { + const archives = [ + { path: `${opts.archiveRoot}/metricbeat`, variant: 'Metricbeat (.monitoring-*)' }, + { path: `${opts.archiveRoot}/package`, variant: 'Package (metrics-*)' }, + ]; + const { setup, tearDown } = getLifecycleMethods(opts.getService); + + return function executeTest(assert: () => void) { + describe(opts.testName, function () { + archives.forEach(({ path, variant }) => { + describe(variant, function () { + before('load archive', () => setup(path)); + after('unload archive', () => tearDown()); + + assert(); + }); + }); + }); + }; +} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 766d623c72f0..282e00b14652 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -14,6 +14,7 @@ "./api_integration/apis/logstash/pipeline/fixtures/*.json", "./api_integration/apis/logstash/pipelines/fixtures/*.json", "./api_integration/apis/telemetry/fixtures/*.json", + "./monitoring_api_integration/fixtures/**/*.json", "../../typings/**/*", "../../packages/kbn-test/types/ftr_globals/**/*", ], @@ -109,5 +110,6 @@ "@kbn/core-saved-objects-common", "@kbn/alerting-fixture-plugin", "@kbn/user-profile-components", + "@kbn/utils", ] } From 72d41749b496bd78b5cca71a458fed01489dca6e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:22:48 -0600 Subject: [PATCH 02/30] Update dependency @babel/traverse to ^7.20.10 (main) (#148238) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@babel/traverse](https://babel.dev/docs/en/next/babel-traverse) ([source](https://togithub.com/babel/babel)) | [`^7.20.8` -> `^7.20.10`](https://renovatebot.com/diffs/npm/@babel%2ftraverse/7.20.10/7.20.10) | [![age](https://badges.renovateapi.com/packages/npm/@babel%2ftraverse/7.20.10/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@babel%2ftraverse/7.20.10/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@babel%2ftraverse/7.20.10/compatibility-slim/7.20.10)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@babel%2ftraverse/7.20.10/confidence-slim/7.20.10)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/elastic/kibana). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Jonathan Budzenski --- package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c4706afabf3e..f3778d118c10 100644 --- a/package.json +++ b/package.json @@ -717,7 +717,7 @@ "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.18.6", "@babel/register": "^7.18.9", - "@babel/traverse": "^7.20.8", + "@babel/traverse": "^7.20.10", "@babel/types": "^7.20.7", "@bazel/ibazel": "^0.16.2", "@bazel/typescript": "4.6.2", diff --git a/yarn.lock b/yarn.lock index 45558eda0553..bd65148bb47e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1197,7 +1197,7 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.10.3", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.20.8", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.10.3", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.19.0", "@babel/traverse@^7.19.1", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.2": version "7.20.10" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.10.tgz#2bf98239597fcec12f842756f186a9dde6d09230" integrity sha512-oSf1juCgymrSez8NI4A2sr4+uB/mFd9MXplYGPEBnfAuWmmyeVcHa6xLPiaRBcXkcb/28bgxmQLTVwFKE1yfsg== From 54631c27db4f664577e58e7ffb0a3b49765b0908 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:23:07 -0600 Subject: [PATCH 03/30] Update @storybook to ^6.5.15 (main) (#148127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@storybook/addon-a11y](https://togithub.com/storybookjs/storybook/tree/main/addons/a11y) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2faddon-a11y/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-a11y/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-a11y/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-a11y/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-a11y/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/addon-actions](https://togithub.com/storybookjs/storybook/tree/main/addons/actions) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2faddon-actions/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-actions/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-actions/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-actions/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-actions/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/addon-controls](https://togithub.com/storybookjs/storybook/tree/next/addons/controls) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2faddon-controls/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-controls/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-controls/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-controls/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-controls/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/addon-docs](https://togithub.com/storybookjs/storybook/tree/main/addons/docs) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2faddon-docs/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-docs/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-docs/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-docs/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-docs/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/addon-essentials](https://togithub.com/storybookjs/storybook/tree/main/addons/essentials) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2faddon-essentials/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-essentials/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-essentials/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-essentials/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-essentials/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/addon-storyshots](https://togithub.com/storybookjs/storybook/tree/main/addons/storyshots/storyshots-core) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2faddon-storyshots/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-storyshots/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-storyshots/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-storyshots/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2faddon-storyshots/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/addons](https://togithub.com/storybookjs/storybook/tree/main/lib/addons) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2faddons/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2faddons/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2faddons/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2faddons/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2faddons/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/api](https://togithub.com/storybookjs/storybook/tree/main/lib/api) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fapi/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fapi/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fapi/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fapi/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fapi/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/client-api](https://togithub.com/storybookjs/storybook/tree/main/lib/client-api) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fclient-api/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fclient-api/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fclient-api/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fclient-api/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fclient-api/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/components](https://togithub.com/storybookjs/storybook/tree/main/lib/components) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fcomponents/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fcomponents/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fcomponents/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fcomponents/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fcomponents/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/core](https://togithub.com/storybookjs/storybook/tree/main/lib/core) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fcore/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fcore/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fcore/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fcore/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fcore/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/core-common](https://togithub.com/storybookjs/storybook/tree/main/lib/core) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fcore-common/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-common/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-common/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-common/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-common/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/core-events](https://togithub.com/storybookjs/storybook/tree/main/lib/core-events) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fcore-events/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-events/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-events/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-events/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fcore-events/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/node-logger](https://togithub.com/storybookjs/storybook/tree/main/lib/node-logger) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fnode-logger/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fnode-logger/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fnode-logger/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fnode-logger/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fnode-logger/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/preview-web](https://togithub.com/storybookjs/storybook/tree/main/lib/preview-web) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2fpreview-web/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2fpreview-web/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2fpreview-web/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2fpreview-web/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2fpreview-web/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/react](https://togithub.com/storybookjs/storybook/tree/main/app/react) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2freact/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2freact/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2freact/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2freact/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2freact/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | | [@storybook/theming](https://togithub.com/storybookjs/storybook/tree/main/lib/theming) ([source](https://togithub.com/storybookjs/storybook)) | [`^6.5.14` -> `^6.5.15`](https://renovatebot.com/diffs/npm/@storybook%2ftheming/6.5.14/6.5.15) | [![age](https://badges.renovateapi.com/packages/npm/@storybook%2ftheming/6.5.15/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@storybook%2ftheming/6.5.15/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@storybook%2ftheming/6.5.15/compatibility-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@storybook%2ftheming/6.5.15/confidence-slim/6.5.14)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/elastic/kibana). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Patryk Kopyciński Co-authored-by: Jonathan Budzenski --- package.json | 34 +-- yarn.lock | 660 +++++++++++++++++++++++++-------------------------- 2 files changed, 347 insertions(+), 347 deletions(-) diff --git a/package.json b/package.json index f3778d118c10..7cb62a0685a7 100644 --- a/package.json +++ b/package.json @@ -808,26 +808,26 @@ "@mapbox/vector-tile": "1.3.1", "@octokit/rest": "^16.35.0", "@openpgp/web-stream-tools": "^0.0.10", - "@storybook/addon-a11y": "^6.5.14", - "@storybook/addon-actions": "^6.5.14", - "@storybook/addon-controls": "^6.5.14", - "@storybook/addon-docs": "^6.5.14", - "@storybook/addon-essentials": "^6.5.14", + "@storybook/addon-a11y": "^6.5.15", + "@storybook/addon-actions": "^6.5.15", + "@storybook/addon-controls": "^6.5.15", + "@storybook/addon-docs": "^6.5.15", + "@storybook/addon-essentials": "^6.5.15", "@storybook/addon-knobs": "^6.4.0", - "@storybook/addon-storyshots": "^6.5.14", - "@storybook/addons": "^6.5.14", - "@storybook/api": "^6.5.14", - "@storybook/client-api": "^6.5.14", - "@storybook/components": "^6.5.14", - "@storybook/core": "^6.5.14", - "@storybook/core-common": "^6.5.14", - "@storybook/core-events": "^6.5.14", - "@storybook/node-logger": "^6.5.14", - "@storybook/preview-web": "^6.5.14", - "@storybook/react": "^6.5.14", + "@storybook/addon-storyshots": "^6.5.15", + "@storybook/addons": "^6.5.15", + "@storybook/api": "^6.5.15", + "@storybook/client-api": "^6.5.15", + "@storybook/components": "^6.5.15", + "@storybook/core": "^6.5.15", + "@storybook/core-common": "^6.5.15", + "@storybook/core-events": "^6.5.15", + "@storybook/node-logger": "^6.5.15", + "@storybook/preview-web": "^6.5.15", + "@storybook/react": "^6.5.15", "@storybook/react-docgen-typescript-plugin": "^1.0.1", "@storybook/testing-react": "^1.3.0", - "@storybook/theming": "^6.5.14", + "@storybook/theming": "^6.5.15", "@testing-library/dom": "^8.19.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.5", diff --git a/yarn.lock b/yarn.lock index bd65148bb47e..7e2f588bb900 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5245,19 +5245,19 @@ "@types/node" ">=8.9.0" axios "^0.21.1" -"@storybook/addon-a11y@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-6.5.14.tgz#3095f87396955f64d86110636e6e2fdf603dd10e" - integrity sha512-nQf+pTcIXZr4dnrn4eDQoIeR62P6aHFFLoNBTcKFsESmHO3KYMC709j6uU1N+GNFMj9SEZYMav3iQC1Ue9BUnw== - dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/channels" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-events" "6.5.14" +"@storybook/addon-a11y@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-6.5.15.tgz#2e36fdadf2b1e0fd68d19a24c537bd2d73ad7cca" + integrity sha512-4IgsCU7mrfooyGSgvyQdkZVu2iGJZqZ+2GDDIzzeIs1yXvuRy6QiHYNzesSrgeL52ykDXaPGuzKu2pcMKfDQHA== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.14" + "@storybook/theming" "6.5.15" axe-core "^4.2.0" core-js "^3.8.2" global "^4.4.0" @@ -5267,18 +5267,18 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-actions@6.5.14", "@storybook/addon-actions@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.14.tgz#29dd6a27e4b373513190ffd8bc89c7a89a7e071c" - integrity sha512-fZt8bn+oCsVDv9yuZfKL4lq77V5EqW60khHpOxLRRK69hMsE+gaylK0O5l/pelVf3Jf3+TablUG+2xWTaJHGlQ== +"@storybook/addon-actions@6.5.15", "@storybook/addon-actions@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.15.tgz#ba737561dbf8a358ea8bc588f3da9fddd1a4267e" + integrity sha512-cnLzVK1S+EydFDSuvxMmMAxVqNXijBGdV9QTgsu6ys5sOkoiXRETKZmxuN8/ZRbkfc4N+1KAylSCZOOHzBQTBQ== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.14" + "@storybook/theming" "6.5.15" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -5292,18 +5292,18 @@ util-deprecate "^1.0.2" uuid-browser "^3.1.0" -"@storybook/addon-backgrounds@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.14.tgz#de34ae41d2700d05acff2929b6b421ee78be417a" - integrity sha512-DZY8oizDTiNLsknyrCyjf6OirwyfrXB4+UCXKXT7Xp+S5PHsdHwBZUADgM6yIwLUjDXzcsL7Ok00C1zI9qERdg== +"@storybook/addon-backgrounds@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.15.tgz#bb97df61a9436f6f5a61f751f4f3fedcfe19fb84" + integrity sha512-9ddB3QIL8mRurf7TvYG1P9i1sW0b8Iik3kGlHggKogHER9WJPzbiUeH0XDjkASSa4rMCZdYn5CZKNkIAoJ2jdA== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.14" + "@storybook/theming" "6.5.15" core-js "^3.8.2" global "^4.4.0" memoizerific "^1.11.3" @@ -5311,47 +5311,47 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-controls@6.5.14", "@storybook/addon-controls@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.14.tgz#c3ad5f716a43032cb982f7b863d5a453ba73c788" - integrity sha512-p16k/69GjwVtnpEiz0fmb1qoqp/H2d5aaSGDt7VleeXsdhs4Kh0kJyxfLpekHmlzT+5IkO08Nm/U8tJOHbw4Hw== +"@storybook/addon-controls@6.5.15", "@storybook/addon-controls@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.15.tgz#2a1f590d5624f658561b8e0ff144667248baf907" + integrity sha512-q5y0TvD0stvQoJZ2PnFmmKIRNSOI4/k2NKyZq//J2cBUBcP1reYlFxdsNwLZWmAFpSIkc2+nsliEzNxU1WByoA== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-common" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-common" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/node-logger" "6.5.14" - "@storybook/store" "6.5.14" - "@storybook/theming" "6.5.14" + "@storybook/node-logger" "6.5.15" + "@storybook/store" "6.5.15" + "@storybook/theming" "6.5.15" core-js "^3.8.2" lodash "^4.17.21" ts-dedent "^2.0.0" -"@storybook/addon-docs@6.5.14", "@storybook/addon-docs@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.14.tgz#5df8bd132890828320cfb5b4769f098142e57a4c" - integrity sha512-gapuzDY+dqgS4/Ap9zj5L76OSExBYtVNYej9xTiF+v0Gh4/kty9FIGlVWiqskffOmixL4nlyImpfsSH8V0JnCw== +"@storybook/addon-docs@6.5.15", "@storybook/addon-docs@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.15.tgz#d5f4b991636953f30daea6aa89d5bfdfa97b9116" + integrity sha512-k3LAu+wVp6pNhfh6B1soCRl6+7sNTNxtqy6WTrIeVJVCGbXbyc5s7gQ48gJ4WAk6meoDEZbypiP4NK1El03YLg== dependencies: "@babel/plugin-transform-react-jsx" "^7.12.12" "@babel/preset-env" "^7.12.11" "@jest/transform" "^26.6.2" "@mdx-js/react" "^1.6.22" - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-common" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/docs-tools" "6.5.14" + "@storybook/docs-tools" "6.5.15" "@storybook/mdx1-csf" "^0.0.1" - "@storybook/node-logger" "6.5.14" - "@storybook/postinstall" "6.5.14" - "@storybook/preview-web" "6.5.14" - "@storybook/source-loader" "6.5.14" - "@storybook/store" "6.5.14" - "@storybook/theming" "6.5.14" + "@storybook/node-logger" "6.5.15" + "@storybook/postinstall" "6.5.15" + "@storybook/preview-web" "6.5.15" + "@storybook/source-loader" "6.5.15" + "@storybook/store" "6.5.15" + "@storybook/theming" "6.5.15" babel-loader "^8.0.0" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -5363,23 +5363,23 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/addon-essentials@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.14.tgz#f0fb5738878e62d58dc9ce0e9305aa7b4488ff8c" - integrity sha512-6fmfDHbp1y/hF0GU0W95RLw4rzN3KGcEcpAZ8HbgTyXIF528j0hhlvkD5AjnQ5dVarlHdKAtMRZA9Y3OCEZD6A== - dependencies: - "@storybook/addon-actions" "6.5.14" - "@storybook/addon-backgrounds" "6.5.14" - "@storybook/addon-controls" "6.5.14" - "@storybook/addon-docs" "6.5.14" - "@storybook/addon-measure" "6.5.14" - "@storybook/addon-outline" "6.5.14" - "@storybook/addon-toolbars" "6.5.14" - "@storybook/addon-viewport" "6.5.14" - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/core-common" "6.5.14" - "@storybook/node-logger" "6.5.14" +"@storybook/addon-essentials@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.15.tgz#cfbf6cacfbc7d14ac30aab118efcac818f0814ba" + integrity sha512-m3EY6BhUk6Z9Et7P5wGaRGNoEDHzJIOsLbGS/4IXvIoDfrqmNIilqUQl8kfDqpVdBSFprvpacHpKpLosu9H37w== + dependencies: + "@storybook/addon-actions" "6.5.15" + "@storybook/addon-backgrounds" "6.5.15" + "@storybook/addon-controls" "6.5.15" + "@storybook/addon-docs" "6.5.15" + "@storybook/addon-measure" "6.5.15" + "@storybook/addon-outline" "6.5.15" + "@storybook/addon-toolbars" "6.5.15" + "@storybook/addon-viewport" "6.5.15" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/node-logger" "6.5.15" core-js "^3.8.2" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" @@ -5401,48 +5401,48 @@ react-lifecycles-compat "^3.0.4" react-select "^3.2.0" -"@storybook/addon-measure@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.14.tgz#3dc5989dd540d23992eadc35e91e6d3a5122c97e" - integrity sha512-7JH35z7NRaNiOMYIG+tJQrQdV2fdURUK94g9x58rNBT4GCtXTclFvhWiwKHHT2CnM8xdYuKGMt3DY9U0urq3Gg== +"@storybook/addon-measure@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.15.tgz#a174bf498168e52469f602a8d6ba6160d4b8a9d6" + integrity sha512-j77WX/v6qpWK8ZuYscWLIc+Am4/WOJRsVgyXLIw1EZIviQsjoXPo7mmyoTneEIbbHfPtWlLRbtmkjh8DAVDrCA== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" global "^4.4.0" -"@storybook/addon-outline@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.14.tgz#b14fe67e90efc2757f406f01841f63686b8f3c41" - integrity sha512-eXkkRmSxfPIcztfcLxGO1Cj61Ohx4qKuYSjNh25CRc+HYbBjQkWyxOXZP+x4Ritx4IuQsgWiCJJO3/zdgXLRgw== +"@storybook/addon-outline@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.15.tgz#7cf9127f3d321136d6780ea2db525b4fd2678281" + integrity sha512-8yGEZQOYypnliU3rsakoZlgT4Pkq8iOhX9JclVXZL/fJMQWFQGCsXqlLaRn8sx7qsa+21PPxY5bd2+Hv/Au4zQ== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-storyshots@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-6.5.14.tgz#b8c083e0f327d406527eef0c90adb68f09ce6973" - integrity sha512-BSYt+GyMeTlxCwMNVwsmfetKjeIZVVRFdhvtyTuSf9MvikBq+SXw6IihkeWbX2g6pssCz9Wc+s6rRK/HJpqTlA== +"@storybook/addon-storyshots@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-6.5.15.tgz#b0f2e33c29f3b454b9a88a64ebc66f5f93fce854" + integrity sha512-tM8y4yyW5dEEp/6EU5HlmCESU4fUs6ETyv9FzdEn2oCNfaBYUvQcJKkd6xkm32LkhTyeg2Ry/IXEswQFSOXSIQ== dependencies: "@jest/transform" "^26.6.2" - "@storybook/addons" "6.5.14" + "@storybook/addons" "6.5.15" "@storybook/babel-plugin-require-context-hook" "1.0.1" - "@storybook/client-api" "6.5.14" - "@storybook/core" "6.5.14" - "@storybook/core-client" "6.5.14" - "@storybook/core-common" "6.5.14" + "@storybook/client-api" "6.5.15" + "@storybook/core" "6.5.15" + "@storybook/core-client" "6.5.15" + "@storybook/core-common" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" "@types/glob" "^7.1.3" "@types/jest" "^26.0.16" @@ -5458,65 +5458,65 @@ regenerator-runtime "^0.13.7" ts-dedent "^2.0.0" -"@storybook/addon-toolbars@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.14.tgz#200bb8cfc771bb8d14c61d7810b02765a473458e" - integrity sha512-BZGQ9YadVRtSd5mpmrwnJta0wK1leX/vgziJX4gUKX2A5JX7VWsiswUGVukLVtE9Oa1jp3fJXE3O5Ip9moj0Ag== +"@storybook/addon-toolbars@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.15.tgz#6c11c9cbcc958ffe881c04cd5f04c2f47ed22f44" + integrity sha512-btwDTgElmaaT0dBRASABbTpq6m1UiQXQmLUmxfjLxVC3I2SK5tyJKbPQ2hVLFAQHK4cQn4u45BxdZ5LDpJ830A== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/theming" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/theming" "6.5.15" core-js "^3.8.2" regenerator-runtime "^0.13.7" -"@storybook/addon-viewport@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.14.tgz#992cc3a61cd231a6c5cdb76c9c206aad8d1ffdfb" - integrity sha512-QtcQZe5ahWcMr4oRgfjeCIJtweTitArc8x1cDfS8maEEy965JJGjrR9xBIOLaw6IEiRtBuvrewYAWbRLLsUE+g== - dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-events" "6.5.14" - "@storybook/theming" "6.5.14" +"@storybook/addon-viewport@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.15.tgz#c30d0f865d86658c380d31f1ae3589392046e23b" + integrity sha512-oOiVzgFMlTnzPLVoHWQNzWdmpksrUyT6Aq8ZOyBPNMQ0RN2doIgFr7W53nZ1OBB5cPQx9q2FgWwzJ7Tawo+iVA== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/theming" "6.5.15" core-js "^3.8.2" global "^4.4.0" memoizerific "^1.11.3" prop-types "^15.7.2" regenerator-runtime "^0.13.7" -"@storybook/addons@6.5.14", "@storybook/addons@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.14.tgz#855ddd85533ffa596b7684f3df7b91b833fdf3f7" - integrity sha512-8wVy1eDKipj+dmWpVmmPa1p2jYVqDvrkWll4IsP/KU7AYFCiyCiVAd1ZPDv9EhDnwArfYYjrdJjAl6gmP0UMag== +"@storybook/addons@6.5.15", "@storybook/addons@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.15.tgz#3c3fafbf3c9ce2182d652cb6682f6581ba6580e1" + integrity sha512-xT31SuSX+kYGyxCNK2nqL7WTxucs3rSmhiCLovJcUjYk+QquV3c2c53Ki7lwwdDbzfXFcNAe0HJ4hoTN4jhn0Q== dependencies: - "@storybook/api" "6.5.14" - "@storybook/channels" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/api" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.14" - "@storybook/theming" "6.5.14" + "@storybook/router" "6.5.15" + "@storybook/theming" "6.5.15" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" global "^4.4.0" regenerator-runtime "^0.13.7" -"@storybook/api@6.5.14", "@storybook/api@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.14.tgz#e0caaee2d8634857ac428acc93db62aba744c1c1" - integrity sha512-RpgEWV4mxD1mNsGWkjSNq3+B/LFNIhXZc4OapEEK5u0jgCZKB7OCsRL9NJZB5WfpyN+vx8SwbUTgo8DIkes3qw== +"@storybook/api@6.5.15", "@storybook/api@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.15.tgz#a189dac82a57ae9cfac43c887207b1075a2a2e96" + integrity sha512-BBE0KXKvj1/3jTghbIoWfrcDM0t+xO7EYtWWAXD6XlhGsZVD2Dy82Z52ONyLulMDRpMWl0OYy3h6A1YnFUH25w== dependencies: - "@storybook/channels" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/router" "6.5.14" + "@storybook/router" "6.5.15" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.5.14" + "@storybook/theming" "6.5.15" core-js "^3.8.2" fast-deep-equal "^3.1.3" global "^4.4.0" @@ -5533,28 +5533,28 @@ resolved "https://registry.yarnpkg.com/@storybook/babel-plugin-require-context-hook/-/babel-plugin-require-context-hook-1.0.1.tgz#0a4ec9816f6c7296ebc97dd8de3d2b7ae76f2e26" integrity sha512-WM4vjgSVi8epvGiYfru7BtC3f0tGwNs7QK3Uc4xQn4t5hHQvISnCqbNrHdDYmNW56Do+bBztE8SwP6NGUvd7ww== -"@storybook/builder-webpack4@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.14.tgz#ff9e1f7b08c112462596ca9a30ce935669147894" - integrity sha512-0pv8BlsMeiP9VYU2CbCZaa3yXDt1ssb8OeTRDbFC0uFFb3eqslsH68I7XsC8ap/dr0RZR0Edtw0OW3HhkjUXXw== +"@storybook/builder-webpack4@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.15.tgz#8050b2eec84e055eee9b181e067d9a8aa76e252a" + integrity sha512-1ZkMECUUdiYplhlgyUF5fqW3XU7eWNDJbuPUguyDOeidgJ111WZzBcLuKj+SNrzdNNgXwROCWAFybiNnX33YHQ== dependencies: "@babel/core" "^7.12.10" - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/channel-postmessage" "6.5.14" - "@storybook/channels" "6.5.14" - "@storybook/client-api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-common" "6.5.14" - "@storybook/core-events" "6.5.14" - "@storybook/node-logger" "6.5.14" - "@storybook/preview-web" "6.5.14" - "@storybook/router" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/preview-web" "6.5.15" + "@storybook/router" "6.5.15" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.14" - "@storybook/theming" "6.5.14" - "@storybook/ui" "6.5.14" + "@storybook/store" "6.5.15" + "@storybook/theming" "6.5.15" + "@storybook/ui" "6.5.15" "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" autoprefixer "^9.8.6" @@ -5586,51 +5586,51 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.2.2" -"@storybook/channel-postmessage@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.14.tgz#17d6071b3563819092ef3e18f8ca7946c2532823" - integrity sha512-0Cmdze5G3Qwxf7yYPGlJxGiY+KiEUQ+8GfpohsKGfvrP8cfSrx6VhxupHA7hDNyRh75hqZq5BrkW4HO9Ypbt5A== +"@storybook/channel-postmessage@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.15.tgz#a9d614be56bededf7cec41b833c46d35958b6d2b" + integrity sha512-gMpA8LWT8lC4z5KWnaMh03aazEwtDO7GtY5kZVru+EEMgExGmaR82qgekwmLmgLj2nRJEv0o138o9IqYUcou8w== dependencies: - "@storybook/channels" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" core-js "^3.8.2" global "^4.4.0" qs "^6.10.0" telejson "^6.0.8" -"@storybook/channel-websocket@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.14.tgz#8b24fcf61a32623f2d03c6f3f962fc24855cff6e" - integrity sha512-ZyDL5PBFWuFQ15NBljhbOaD/3FAijXvLj5oxfNris2khdkqlP6/8JmcIvfohJJcqepGZHUF9H29OaUsRC35ftA== +"@storybook/channel-websocket@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.15.tgz#3fa090412d122428a7bc3d622e4cd36fdac7dd78" + integrity sha512-K85KEgzo5ahzJNJjyUbSNyuRmkeC8glJX2hCg2j9HiJ9rasX53qugkODrKDlWAeheulo3kR13VSuAqIuwVbmbw== dependencies: - "@storybook/channels" "6.5.14" - "@storybook/client-logger" "6.5.14" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" core-js "^3.8.2" global "^4.4.0" telejson "^6.0.8" -"@storybook/channels@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.14.tgz#dcb73496a771dafb77a9e25dce2ba1adaa352df9" - integrity sha512-hHpr4Sya6fuEDhy7vnfD2QnL5wy1CaAK9BC0FLupndXnQyKJtygfIaUP4a0B2KntuNPbzPhclb2Hb4yM7CExmQ== +"@storybook/channels@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.15.tgz#586681b6ec458124da084c39bc8c518d9e96b10b" + integrity sha512-gPpsBgirv2NCXbH4WbYqdkI0JLE96aiVuu7UEWfn9yu071pQ9CLHbhXGD9fSFNrfOkyBBY10ppSE7uCXw3Wexg== dependencies: core-js "^3.8.2" ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-api@6.5.14", "@storybook/client-api@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.14.tgz#57a660810165126cdf3380cd04bf6c5f027eab5c" - integrity sha512-G5mBQCKn8/VqE9XDCL19ixcvu8YhaQZ0AE+EXGYXUsvPpyQ43oGoGJry5IqOzeRlc7dbglFWpMkB6PeeUD7aCw== +"@storybook/client-api@6.5.15", "@storybook/client-api@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.15.tgz#70f3ced6d0fcc7b71217cae858bf81c3b2c50eba" + integrity sha512-0ZGpRgVz7rdbCguBqBpwObXbsVY5qlSTWDzzIBpmz8EkxW/MtK5wEyeq+0L0O+DTn41FwvH5yCGLAENpzWD8BQ== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/channel-postmessage" "6.5.14" - "@storybook/channels" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.14" + "@storybook/store" "6.5.15" "@types/qs" "^6.9.5" "@types/webpack-env" "^1.16.0" core-js "^3.8.2" @@ -5645,43 +5645,43 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/client-logger@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.14.tgz#ec178f31e70969ae22399ce4207c05e4f1d480a8" - integrity sha512-r1pY69DGKzX9/GngkudthaaPxPlka16zjG7Y58psunwcoUuH3riAP1cjqhXt5+S8FKCNI/MGb82PLlCPX2Liuw== +"@storybook/client-logger@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.15.tgz#0d9878af893a3493b6ee108cc097ae1436d7da4d" + integrity sha512-0uyxKvodq+FycGv6aUwC1wUR6suXf2+7ywMFAOlYolI4UvNj8NyU/5AfgKT5XnxYAgPmoCiAjOE700TrfHrosw== dependencies: core-js "^3.8.2" global "^4.4.0" -"@storybook/components@6.5.14", "@storybook/components@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.14.tgz#e4d35b689674a16d88d0d16f50319865387497dd" - integrity sha512-wqB9CF3sjxtgffnDW1G/W5SsKumsFQ0ftn/3PdrsvKULu5LM5bjNEqC2cTCWrk9vQhj+EVQxzdVM/BlPl/lSwg== +"@storybook/components@6.5.15", "@storybook/components@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.15.tgz#8145be807bf48c1d010f29114411f390a9e3228f" + integrity sha512-bHTT0Oa3s4g+MBMaLBbX9ofMtb1AW59AzIUNGrfqW1XqJMGuUHMiJ7TSo+i5dRSFpbFygnwMEG9LfHxpR2Z0Dw== dependencies: - "@storybook/client-logger" "6.5.14" + "@storybook/client-logger" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/theming" "6.5.14" + "@storybook/theming" "6.5.15" core-js "^3.8.2" memoizerific "^1.11.3" qs "^6.10.0" regenerator-runtime "^0.13.7" util-deprecate "^1.0.2" -"@storybook/core-client@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.14.tgz#ff8bd155750ca4644dba7e8ac53ed3ad1c414bda" - integrity sha512-d5mUgz1xSvrAdal8XKI5YOZOM/XUly90vis3DboeZRO58qSp+NH5xFYIBBED5qefDgmGU0Yv4rXHQlph96LSHQ== - dependencies: - "@storybook/addons" "6.5.14" - "@storybook/channel-postmessage" "6.5.14" - "@storybook/channel-websocket" "6.5.14" - "@storybook/client-api" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core-events" "6.5.14" +"@storybook/core-client@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.15.tgz#57d86a07f920ab0cb058820dbcb1f48cc9dc6257" + integrity sha512-i9t4WONy2MxJwLI1FIp5ck7b52EXyJfALnxUn4O/3GTkw09J0NOKi2DPjefUsi7IB5MzFpDjDH9vw/XiTM+OZw== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/channel-websocket" "6.5.15" + "@storybook/client-api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/preview-web" "6.5.14" - "@storybook/store" "6.5.14" - "@storybook/ui" "6.5.14" + "@storybook/preview-web" "6.5.15" + "@storybook/store" "6.5.15" + "@storybook/ui" "6.5.15" airbnb-js-shims "^2.2.1" ansi-to-html "^0.6.11" core-js "^3.8.2" @@ -5693,10 +5693,10 @@ unfetch "^4.2.0" util-deprecate "^1.0.2" -"@storybook/core-common@6.5.14", "@storybook/core-common@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.14.tgz#162f321d0c3011ece84b324f584c641c474e20d9" - integrity sha512-MrxhYXYrtN6z/+tydjPkCIwDQm5q8Jx+w4TPdLKBZu7vzfp6T3sT12Ym96j9MJ42CvE4vSDl/Njbw6C0D+yEVw== +"@storybook/core-common@6.5.15", "@storybook/core-common@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.15.tgz#3ab524c7abdae52024caeb5c0349a764cb08769f" + integrity sha512-uits9o6qwHTPnjsNZP25f7hWmUBGRJ7FXtxxtEaNSmtiwk50KWxBaro7wt505lJ1Gb9vOhpNPhS7y3IxdsXNmQ== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-proposal-class-properties" "^7.12.1" @@ -5720,7 +5720,7 @@ "@babel/preset-react" "^7.12.10" "@babel/preset-typescript" "^7.12.7" "@babel/register" "^7.12.1" - "@storybook/node-logger" "6.5.14" + "@storybook/node-logger" "6.5.15" "@storybook/semver" "^7.3.2" "@types/node" "^14.0.10 || ^16.0.0" "@types/pretty-hrtime" "^1.0.0" @@ -5749,30 +5749,30 @@ util-deprecate "^1.0.2" webpack "4" -"@storybook/core-events@6.5.14", "@storybook/core-events@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.14.tgz#5b4f94d336cd14f0e8e213a0f3cf9a098c45d1dc" - integrity sha512-PLu0M8Mqt9ruN5RupgcFKHEybiSm3CdWQyylWO5FRGg+WZV3BCm0aI8ujvO1GAm+YEi57Lull+M9d6NUycTpRg== +"@storybook/core-events@6.5.15", "@storybook/core-events@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.15.tgz#c12f645b50231c50eb9b26038aa67ab92b1ba24e" + integrity sha512-B1Ba6l5W7MeNclclqMMTMHgYgfdpB5SIhNCQFnzIz8blynzRhNFMdxvbAl6Je5G0S4xydYYd7Lno2kXQebs7HA== dependencies: core-js "^3.8.2" -"@storybook/core-server@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.14.tgz#ba9ca39b41879aa657c524e3c0ac4c7b1abe9bbd" - integrity sha512-+Z3lHEsDpiBXt6xBwU5AVBoEkicndnHoiLwhEGPkfixy7POYEEny3cm54tteVxV8O5AHMwsHs54/QD+hHxAXnQ== +"@storybook/core-server@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.15.tgz#217c40d7a33708b9ac69f73dd51ed9d2f031d19d" + integrity sha512-m+pZwHhCjwryeqTptyyKHSbIjnnPGKoRSnkqLTOpKQf8llZMnNQWUFrn4fx6UDKzxFQ2st2+laV8O2QbMs8qwQ== dependencies: "@discoveryjs/json-ext" "^0.5.3" - "@storybook/builder-webpack4" "6.5.14" - "@storybook/core-client" "6.5.14" - "@storybook/core-common" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/builder-webpack4" "6.5.15" + "@storybook/core-client" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/csf-tools" "6.5.14" - "@storybook/manager-webpack4" "6.5.14" - "@storybook/node-logger" "6.5.14" + "@storybook/csf-tools" "6.5.15" + "@storybook/manager-webpack4" "6.5.15" + "@storybook/node-logger" "6.5.15" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.14" - "@storybook/telemetry" "6.5.14" + "@storybook/store" "6.5.15" + "@storybook/telemetry" "6.5.15" "@types/node" "^14.0.10 || ^16.0.0" "@types/node-fetch" "^2.5.7" "@types/pretty-hrtime" "^1.0.0" @@ -5807,18 +5807,18 @@ ws "^8.2.3" x-default-browser "^0.4.0" -"@storybook/core@6.5.14", "@storybook/core@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.14.tgz#8fa21c7539e2ffe8f9601d9542b0e627f7f9a349" - integrity sha512-5rjwZXk++NkKWCmHt/CC+h2L4ZbOYkLJpMmaB97CwgQCA6kaF8xuJqlAwG72VUH3oV+6RntW02X6/ypgX1atPw== +"@storybook/core@6.5.15", "@storybook/core@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.15.tgz#82e0998d908fc9e66a659e1217072c425d63f9b6" + integrity sha512-T9TjLxbb5P/XvLEoj0dnbtexJa0V3pqCifRlIUNkTJO0nU3PdGLMcKMSyIYWjkthAJ9oBrajnodV0UveM/epTg== dependencies: - "@storybook/core-client" "6.5.14" - "@storybook/core-server" "6.5.14" + "@storybook/core-client" "6.5.15" + "@storybook/core-server" "6.5.15" -"@storybook/csf-tools@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.14.tgz#fe36c8570c1f9e27aa4536f09a729a61598a1255" - integrity sha512-PgCKgyfD6UD9aNilDxmKJRbCZwcZl8t8Orb6+vVGuzB5f0BV92NqnHS4sgAlFoZ+iqcQGUEU9vRIdUxNcyItaw== +"@storybook/csf-tools@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.15.tgz#dc5d0fe946c25d60bf201e5180c4fc81b24f763b" + integrity sha512-2LwSD7yE/ccXBc58K4vdKw/oJJg6IpC4WD51rBt2mAl5JUCkxhOq7wG/Z8Wy1lZw2LVuKNTmjVou5blGRI/bTg== dependencies: "@babel/core" "^7.12.10" "@babel/generator" "^7.12.11" @@ -5849,33 +5849,33 @@ dependencies: lodash "^4.17.15" -"@storybook/docs-tools@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.14.tgz#68d9c156cdc80a906570807f1d116be103032656" - integrity sha512-qA0UWvrZ7XyIWD+01NGHiiGPSbfercrxjphM9wHgF6KrO6e5iykNKIEL4elsM+EV4szfhlalQdtpnwM7WtXODA== +"@storybook/docs-tools@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.15.tgz#c9a3954719c45c3748abd6aaa735e33f5c961912" + integrity sha512-8w78NFOtlJGuIa9vPPsr87J9iQUGmLFh7CrMS2+t9LxW+0oH5MZ8QqPQUHNuTuKsYN3r+QAmmi2pj0auZmCoKA== dependencies: "@babel/core" "^7.12.10" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.14" + "@storybook/store" "6.5.15" core-js "^3.8.2" doctrine "^3.0.0" lodash "^4.17.21" regenerator-runtime "^0.13.7" -"@storybook/manager-webpack4@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.14.tgz#92cd3d34b9e28d7edd215b1e79b1f0757fb66eba" - integrity sha512-ixfJuaG0eiOlxn4i+LJNRUZkm+3WMsiaGUm0hw2XHF0pW3cBIA/+HyzkEwVh/fROHbsOERTkjNl0Ygl12Imw9w== +"@storybook/manager-webpack4@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.15.tgz#09808b87b510591390765af708ab511ff63a1e5c" + integrity sha512-zRvBTMoaFO6MvHDsDLnt3tsFENhpY3k+e/UIPdqbIDMbUPGGQzxJucAM9aS/kbVSO5IVs8IflVxbeeB/uTIIfA== dependencies: "@babel/core" "^7.12.10" "@babel/plugin-transform-template-literals" "^7.12.1" "@babel/preset-react" "^7.12.10" - "@storybook/addons" "6.5.14" - "@storybook/core-client" "6.5.14" - "@storybook/core-common" "6.5.14" - "@storybook/node-logger" "6.5.14" - "@storybook/theming" "6.5.14" - "@storybook/ui" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/core-client" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/theming" "6.5.15" + "@storybook/ui" "6.5.15" "@types/node" "^14.0.10 || ^16.0.0" "@types/webpack" "^4.41.26" babel-loader "^8.0.0" @@ -5920,10 +5920,10 @@ prettier ">=2.2.1 <=2.3.0" ts-dedent "^2.0.0" -"@storybook/node-logger@6.5.14", "@storybook/node-logger@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.14.tgz#5d85c475c0afd4124f86fae559bcb35db92f35b2" - integrity sha512-MbEEgUEfrDN8Y0vzZJqPcxwWvX0l8zAsXy6d/DORP2AmwuNmnWTy++BE9YhxH6HMdM1ivRDmBbT30+KBUWhnUA== +"@storybook/node-logger@6.5.15", "@storybook/node-logger@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.15.tgz#d99695e8d5f8cf434e8fdcca719b5b5fa5c88e2e" + integrity sha512-LQjjbfMuUXm7wZTBKb+iGeCNnej4r1Jb2NxG3Svu2bVhaoB6u33jHAcbmhXpXW1jghzW3kQwOU7BoLuJiRRFIw== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.1.0" @@ -5931,24 +5931,24 @@ npmlog "^5.0.1" pretty-hrtime "^1.0.3" -"@storybook/postinstall@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.14.tgz#492d45008c39c4274dfc1a69ef5f31981e913f99" - integrity sha512-vtnQczSSkz7aPIc2dsDaZWlCDAcJb258KGXk72w7MEY9/zLlr6tdQLI30B6SkRNFnR8fQQf4H2gbFq/GM0EF5A== +"@storybook/postinstall@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.15.tgz#90989c8049357a6df6f38c933172210c9c6caf8c" + integrity sha512-l7pApTgLD10OedNOyuf4vUdVCHLOSaIUIL9gdJl1WaSFHiUpJvvzBIh5M4aRICYPbnuExQc8y2GAjERKO4Ep+g== dependencies: core-js "^3.8.2" -"@storybook/preview-web@6.5.14", "@storybook/preview-web@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.14.tgz#acfd5e3ba72a00405f9ad5925a9a7844b36602c4" - integrity sha512-ey2E7222xw0itPgCWH7ZIrdgM1yCdYte/QxRvwv/O4us4SUs/RQaL1aoCD+hCRwd0BNyZUk/u1KnqB4y0MnHww== +"@storybook/preview-web@6.5.15", "@storybook/preview-web@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.15.tgz#5f47899dff1580ed3dc1b5a7bfdf67d6574536fc" + integrity sha512-gIHABSAD0JS0iRaG67BnSDq/q8Zf4fFwEWBQOSYgcEx2TzhAUeSkhGZUQHdlOTCwuA2PpXT0/cWDH8u2Ev+msg== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/channel-postmessage" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/store" "6.5.14" + "@storybook/store" "6.5.15" ansi-to-html "^0.6.11" core-js "^3.8.2" global "^4.4.0" @@ -5986,24 +5986,24 @@ react-docgen-typescript "^2.0.0" tslib "^2.0.0" -"@storybook/react@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.14.tgz#9e5a93958c410c3f9d21374e30f68ac2960a9c91" - integrity sha512-SL0P5czN3g/IZAYw8ur9I/O8MPZI7Lyd46Pw+B1f7+Ou8eLmhqa8Uc8+3fU6v7ohtUDwsBiTsg3TAfTVEPog4A== +"@storybook/react@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.15.tgz#83e645b16a4d241ec84a8d0015b1a7a2d55c5091" + integrity sha512-iQta2xOs/oK0sw/zpn3g/huvOmvggzi8z2/WholmUmmRiSQRo9lOhRXH0u13T4ja4fEa+u7m58G83xOG6i73Kw== dependencies: "@babel/preset-flow" "^7.12.1" "@babel/preset-react" "^7.12.10" "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" - "@storybook/addons" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core" "6.5.14" - "@storybook/core-common" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core" "6.5.15" + "@storybook/core-common" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" - "@storybook/docs-tools" "6.5.14" - "@storybook/node-logger" "6.5.14" + "@storybook/docs-tools" "6.5.15" + "@storybook/node-logger" "6.5.15" "@storybook/react-docgen-typescript-plugin" "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" "@storybook/semver" "^7.3.2" - "@storybook/store" "6.5.14" + "@storybook/store" "6.5.15" "@types/estree" "^0.0.51" "@types/node" "^14.14.20 || ^16.0.0" "@types/webpack-env" "^1.16.0" @@ -6027,12 +6027,12 @@ util-deprecate "^1.0.2" webpack ">=4.43.0 <6.0.0" -"@storybook/router@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.14.tgz#8cb959c8cfece2a948cd6a4e14ac0fa1cece3095" - integrity sha512-AvHbpRUAHnzm5pmwFPjDR09uPjQITD6kA0QNa2pe+7/Q/b4k40z5dHvHZJ/YhWhwVwGqGBG20KdDOl30wLXAZw== +"@storybook/router@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.15.tgz#bf01d35bdd4603bf188629a6578489e313a312fd" + integrity sha512-9t8rI8t7/Krolau29gsdjdbRQ66orONIyP0efp0EukVgv6reNFzb/U14ARrl0uHys6Tl5Xyece9FoakQUdn8Kg== dependencies: - "@storybook/client-logger" "6.5.14" + "@storybook/client-logger" "6.5.15" core-js "^3.8.2" memoizerific "^1.11.3" qs "^6.10.0" @@ -6046,13 +6046,13 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.14.tgz#cf4e78166a40edd7dd3df3563face750f70c29df" - integrity sha512-0GKMZ6IMVGxfQn/RYdRdnzxCe4+zZsxHBY9SQB2bbYWyfjJQ5rCJvmYQuMAuuuUmXBv9gk50iJLwai+lb4tbFg== +"@storybook/source-loader@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.15.tgz#363a9b2812a2cef6cc5cb948e55d8a2624e18643" + integrity sha512-MaWzki40g0/7NWmJgUBhOp+e7y8Ohw6G/bRr/rcDP7eXSnud6ThYylXv0QqBScLPPTy8txjmBClCoqdLGyvLWQ== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/client-logger" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/client-logger" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" estraverse "^5.2.0" @@ -6062,14 +6062,14 @@ prettier ">=2.2.1 <=2.3.0" regenerator-runtime "^0.13.7" -"@storybook/store@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.14.tgz#92b67aac8c6a55beff934e135937a1c6e1e77879" - integrity sha512-s07Vw4nbShPYwBJmVXzptuyCkrDQD3khcrKB5L7NsHHgWsm2QI0OyiPMuMbSvgipjcMc/oRqdL3tFUeFak9EMg== +"@storybook/store@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.15.tgz#d6ca7a3165442aabfde335f243bdd179d53bca1a" + integrity sha512-r6cYTf6GtbqgdI4ZG3xuWdJAAu5fJ3xAWMiDkHyoK2u+R2TeYXIsJvgn0BPrW87sZhELIkg4ckdFECmATs3kpQ== dependencies: - "@storybook/addons" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/core-events" "6.5.14" + "@storybook/addons" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" "@storybook/csf" "0.0.2--canary.4566f4d.1" core-js "^3.8.2" fast-deep-equal "^3.1.3" @@ -6083,13 +6083,13 @@ ts-dedent "^2.0.0" util-deprecate "^1.0.2" -"@storybook/telemetry@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.14.tgz#b2e8a99ed36fc97451a1f695792a79605b5f7746" - integrity sha512-AVSw7WyKHrVbXMSZZ0fvg3oAb8xAS7OrmNU6++yUfbuqpF0JNtNkNnRSaJ4Nh7Vujzloy5jYhbpfY44nb/hsCw== +"@storybook/telemetry@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.15.tgz#852050c1e54bf704a104e47e4e498d999096e0e7" + integrity sha512-WHMRG6xMkEGscn1q4SotwzV8hxM1g3zHyXPN77iosY5zpOIn/qAzvkmW28V1DPH9jPWMZMizBgG1TIQvUpduXg== dependencies: - "@storybook/client-logger" "6.5.14" - "@storybook/core-common" "6.5.14" + "@storybook/client-logger" "6.5.15" + "@storybook/core-common" "6.5.15" chalk "^4.1.0" core-js "^3.8.2" detect-package-manager "^2.0.1" @@ -6108,30 +6108,30 @@ dependencies: "@storybook/csf" "0.0.2--canary.87bc651.0" -"@storybook/theming@6.5.14", "@storybook/theming@^6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.14.tgz#4cc7e568b9641112f35abe0606cc6925952cb647" - integrity sha512-3ff6RLZGaIil/AFJ0/BRlE2hhdPrC5v6wGbRfroZVmGldRCxio/7+KAA3LH6cuHnjK5MeBcCBaHuxzXqGmbEFw== +"@storybook/theming@6.5.15", "@storybook/theming@^6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.15.tgz#048461b37ad0c29dc8d91a065a6bf1c90067524c" + integrity sha512-pgdW0lVZKKXQ4VhIfLHycMmwFSVOY7vLTKnytag4Y8Yz+aXm0bwDN/QxPntFzDH47F1Rcy2ywNnvty8ooDTvuA== dependencies: - "@storybook/client-logger" "6.5.14" + "@storybook/client-logger" "6.5.15" core-js "^3.8.2" memoizerific "^1.11.3" regenerator-runtime "^0.13.7" -"@storybook/ui@6.5.14": - version "6.5.14" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.14.tgz#8c03a37917adc0060b077d3acb7f0064ace68083" - integrity sha512-dXlCIULh8ytgdFrvVoheQLlZjAyyYmGCuw+6m+s+2yF/oUbFREG/5Zo9hDwlJ4ZiAyqNLkuwg2tnMYtjapZSog== - dependencies: - "@storybook/addons" "6.5.14" - "@storybook/api" "6.5.14" - "@storybook/channels" "6.5.14" - "@storybook/client-logger" "6.5.14" - "@storybook/components" "6.5.14" - "@storybook/core-events" "6.5.14" - "@storybook/router" "6.5.14" +"@storybook/ui@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.15.tgz#e4d80465116e53d48d2c3bb616909c37f5405181" + integrity sha512-OO+TWZmI8ebWA1C3JBKNvbUbsgvt4GppqsGlkf5CTBZrT/MzmMlYiooLAtlY1ZPcMtTd5ynLxvroHWBEnMOk2A== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/router" "6.5.15" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.5.14" + "@storybook/theming" "6.5.15" core-js "^3.8.2" memoizerific "^1.11.3" qs "^6.10.0" From 0b7a089c32c73bc353c63826eb6cd91ebd6152ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 17:24:35 +0200 Subject: [PATCH 04/30] Update dependency @elastic/charts to v51.3.0 (main) (#148051) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [@elastic/charts](https://togithub.com/elastic/elastic-charts) | [`51.1.1` -> `51.3.0`](https://renovatebot.com/diffs/npm/@elastic%2fcharts/51.1.1/51.3.0) | [![age](https://badges.renovateapi.com/packages/npm/@elastic%2fcharts/51.3.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@elastic%2fcharts/51.3.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@elastic%2fcharts/51.3.0/compatibility-slim/51.1.1)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@elastic%2fcharts/51.3.0/confidence-slim/51.1.1)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/elastic/kibana). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marco Vettorello Co-authored-by: Stratoula Kalafateli --- package.json | 2 +- x-pack/test/functional/apps/lens/group2/dashboard.ts | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 7cb62a0685a7..e1f95f67b4d2 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "@dnd-kit/utilities": "^2.0.0", "@elastic/apm-rum": "^5.12.0", "@elastic/apm-rum-react": "^1.4.2", - "@elastic/charts": "51.1.1", + "@elastic/charts": "51.3.0", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.5.0-canary.1", "@elastic/ems-client": "8.3.3", diff --git a/x-pack/test/functional/apps/lens/group2/dashboard.ts b/x-pack/test/functional/apps/lens/group2/dashboard.ts index 074fed0f2252..7eb884839a1e 100644 --- a/x-pack/test/functional/apps/lens/group2/dashboard.ts +++ b/x-pack/test/functional/apps/lens/group2/dashboard.ts @@ -70,7 +70,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardAddPanel.closeAddPanel(); await PageObjects.lens.goToTimeRange(); await retry.try(async () => { - await clickInChart(6, 5); // hardcoded position of bar, depends heavy on data and charts implementation + await clickInChart(30, 5); // hardcoded position of bar, depends heavy on data and charts implementation await testSubjects.existOrFail('applyFiltersPopoverButton', { timeout: 2500 }); }); diff --git a/yarn.lock b/yarn.lock index 7e2f588bb900..b03565fd3c8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1456,10 +1456,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@51.1.1": - version "51.1.1" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-51.1.1.tgz#005c9fce7a2ef0345ac223d18115ad719c2c15e2" - integrity sha512-DJjhHBvovQ/EfAlF7cjAB1fhwjcrt4Iyy/Vxed0BAw6gqoppT7/3Kk8AmUzzj05BK7ZFHlaL/05fe3fUEWuQEw== +"@elastic/charts@51.3.0": + version "51.3.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-51.3.0.tgz#aaf76f380e60fb5e0c944ac6ea333a45f15d7fb2" + integrity sha512-sEeHs99a0IKhKouP0b4rVnMJlwTw7dDvNS5EmTWK8gBsF/GCTrxxoxf1MJtQWvJrOnFEpXrzYvaeiHjmocmZQw== dependencies: "@popperjs/core" "^2.4.0" bezier-easing "^2.1.0" From 9a5688ab69fcb0268e41b6e58a022292418c0564 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Tue, 3 Jan 2023 16:29:22 +0100 Subject: [PATCH 05/30] [Infrastructure UI] Hosts: Right align table number columns and fix spacing between value and unit (test fix) (#148274) Closes [#147805](https://github.com/elastic/kibana/issues/147805) and fixes https://github.com/elastic/kibana/pull/148180 ## Summary This PR aligns right the columns with numbers in the host table and adds space between the value and unit for bytes/bits values ![image](https://user-images.githubusercontent.com/14139027/209950481-7a3a51a1-8786-4261-b835-09ca3e6e4ee2.png) ### Testing Open host view page - The host table number columns are right-aligned - There is a space between the value and unit for bytes/bits values image The formatter will also add space to the inventory page metrics: image Co-authored-by: Jenny Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/infra/common/formatters/bytes.test.ts | 6 +++--- x-pack/plugins/infra/common/formatters/bytes.ts | 4 ++-- .../pages/metrics/hosts/components/hosts_table_columns.tsx | 6 ++++++ .../waffle/__snapshots__/conditional_tooltip.test.tsx.snap | 4 ++-- .../components/helpers/create_formatter_for_metrics.test.ts | 4 ++-- x-pack/test/functional/apps/infra/hosts_view.ts | 4 ++-- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/infra/common/formatters/bytes.test.ts b/x-pack/plugins/infra/common/formatters/bytes.test.ts index 146acbb2a90c..4299f37f40ab 100644 --- a/x-pack/plugins/infra/common/formatters/bytes.test.ts +++ b/x-pack/plugins/infra/common/formatters/bytes.test.ts @@ -11,14 +11,14 @@ import { createBytesFormatter } from './bytes'; describe('createDataFormatter', () => { it('should format bytes as bytesDecimal', () => { const formatter = createBytesFormatter(InfraWaffleMapDataFormat.bytesDecimal); - expect(formatter(1000000)).toBe('1MB'); + expect(formatter(1000000)).toBe('1 MB'); }); it('should format bytes as bitsDecimal', () => { const formatter = createBytesFormatter(InfraWaffleMapDataFormat.bitsDecimal); - expect(formatter(1000000)).toBe('8Mbit'); + expect(formatter(1000000)).toBe('8 Mbit'); }); it('should format bytes as abbreviatedNumber', () => { const formatter = createBytesFormatter(InfraWaffleMapDataFormat.abbreviatedNumber); - expect(formatter(1000000)).toBe('1M'); + expect(formatter(1000000)).toBe('1 M'); }); }); diff --git a/x-pack/plugins/infra/common/formatters/bytes.ts b/x-pack/plugins/infra/common/formatters/bytes.ts index 797168c300f6..50f3b6b0aa4b 100644 --- a/x-pack/plugins/infra/common/formatters/bytes.ts +++ b/x-pack/plugins/infra/common/formatters/bytes.ts @@ -47,7 +47,7 @@ export const createBytesFormatter = (format: InfraWaffleMapDataFormat) => (bytes // is greater then the max label then use the max label. const power = Math.min(Math.floor(Math.log(Math.abs(value)) / Math.log(base)), labels.length - 1); if (power < 0) { - return `${formatNumber(value)}${labels[0]}`; + return `${formatNumber(value)} ${labels[0]}`; } - return `${formatNumber(value / Math.pow(base, power))}${labels[power]}`; + return `${formatNumber(value / Math.pow(base, power))} ${labels[power]}`; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_columns.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_columns.tsx index 7f29e25ff9ec..fed94b67dc99 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_columns.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/hosts_table_columns.tsx @@ -74,6 +74,7 @@ export const HostsTableColumns: Array> = [ render: (cpuCores: SnapshotNodeMetric) => ( <>{formatMetric('cpuCores', cpuCores?.value ?? cpuCores?.max)} ), + align: 'right', }, { name: i18n.translate('xpack.infra.hostsTable.diskLatencyColumnHeader', { @@ -82,6 +83,7 @@ export const HostsTableColumns: Array> = [ field: 'diskLatency.avg', sortable: true, render: (avg: number) => <>{formatMetric('diskLatency', avg)}, + align: 'right', }, { name: i18n.translate('xpack.infra.hostsTable.averageTxColumnHeader', { @@ -90,6 +92,7 @@ export const HostsTableColumns: Array> = [ field: 'tx.avg', sortable: true, render: (avg: number) => <>{formatMetric('tx', avg)}, + align: 'right', }, { name: i18n.translate('xpack.infra.hostsTable.averageRxColumnHeader', { @@ -98,6 +101,7 @@ export const HostsTableColumns: Array> = [ field: 'rx.avg', sortable: true, render: (avg: number) => <>{formatMetric('rx', avg)}, + align: 'right', }, { name: i18n.translate('xpack.infra.hostsTable.averageMemoryTotalColumnHeader', { @@ -106,6 +110,7 @@ export const HostsTableColumns: Array> = [ field: 'memoryTotal.avg', sortable: true, render: (avg: number) => <>{formatMetric('memoryTotal', avg)}, + align: 'right', }, { name: i18n.translate('xpack.infra.hostsTable.averageMemoryUsageColumnHeader', { @@ -114,5 +119,6 @@ export const HostsTableColumns: Array> = [ field: 'memory.avg', sortable: true, render: (avg: number) => <>{formatMetric('memory', avg)}, + align: 'right', }, ]; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap index 5c3b53bb551c..02c8f6aa6ed3 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap @@ -55,7 +55,7 @@ exports[`ConditionalToolTip renders correctly 1`] = ` class="euiFlexItem emotion-euiFlexItem-growZero" data-test-subj="conditionalTooltipContent-value" > - 8Mbit/s + 8 Mbit/s
- 8Mbit/s + 8 Mbit/s
{ field: 'host.network.egress.bytes', }; const format = createFormatterForMetric(metric); - expect(format(103929292)).toBe('831.4Mbit/s'); + expect(format(103929292)).toBe('831.4 Mbit/s'); }); it('should just work for bytes', () => { const metric: MetricsExplorerMetric = { @@ -38,6 +38,6 @@ describe('createFormatterForMetric()', () => { field: 'host.network.egress.bytes', }; const format = createFormatterForMetric(metric); - expect(format(103929292)).toBe('103.9MB'); + expect(format(103929292)).toBe('103.9 MB'); }); }); diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts index 3c00b902211f..2cb5c703d14e 100644 --- a/x-pack/test/functional/apps/infra/hosts_view.ts +++ b/x-pack/test/functional/apps/infra/hosts_view.ts @@ -57,8 +57,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { metric: 'hosts', value: '6' }, { metric: 'cpu', value: '0.8%' }, { metric: 'memory', value: '16.8%' }, - { metric: 'tx', value: '0bit/s' }, - { metric: 'rx', value: '0bit/s' }, + { metric: 'tx', value: '0 bit/s' }, + { metric: 'rx', value: '0 bit/s' }, ].forEach(({ metric, value }) => { it(`${metric} tile should show ${value}`, async () => { const tileValue = await pageObjects.infraHostsView.getMetricsTrendTileValue(metric); From 5f2c0b3c8a21742756891338d4fe72816d61c24f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:35:06 -0600 Subject: [PATCH 06/30] Update dependency core-js to ^3.27.0 (main) (#148259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [core-js](https://togithub.com/zloirock/core-js) | [`^3.26.1` -> `^3.27.0`](https://renovatebot.com/diffs/npm/core-js/3.26.1/3.27.0) | [![age](https://badges.renovateapi.com/packages/npm/core-js/3.27.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/core-js/3.27.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/core-js/3.27.0/compatibility-slim/3.26.1)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/core-js/3.27.0/confidence-slim/3.26.1)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Renovate will not automatically rebase this PR, because other commits have been found. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/elastic/kibana). Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Jonathan Budzenski --- package.json | 2 +- packages/kbn-babel-preset/node_preset.js | 2 +- packages/kbn-babel-preset/webpack_preset.js | 2 +- yarn.lock | 111 ++------------------ 4 files changed, 12 insertions(+), 105 deletions(-) diff --git a/package.json b/package.json index e1f95f67b4d2..8385a3c7696f 100644 --- a/package.json +++ b/package.json @@ -494,7 +494,7 @@ "compare-versions": "3.5.1", "constate": "^3.3.2", "copy-to-clipboard": "^3.0.8", - "core-js": "^3.26.1", + "core-js": "^3.27.0", "cronstrue": "^1.51.0", "cuid": "^2.1.8", "cytoscape": "^3.10.0", diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index aa413a05013f..54efea1cebb5 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -31,7 +31,7 @@ module.exports = (_, options = {}) => { // Because of that we should use for that value the same version we install // in the package.json in order to have the same polyfills between the environment // and the tests - corejs: '3.26.1', + corejs: '3.27.1', bugfixes: true, ...(options['@babel/preset-env'] || {}), diff --git a/packages/kbn-babel-preset/webpack_preset.js b/packages/kbn-babel-preset/webpack_preset.js index 7d8f092384b4..7dfe996b91fa 100644 --- a/packages/kbn-babel-preset/webpack_preset.js +++ b/packages/kbn-babel-preset/webpack_preset.js @@ -19,7 +19,7 @@ module.exports = (api, options = {}) => { modules: false, // Please read the explanation for this // in node_preset.js - corejs: '3.26.1', + corejs: '3.27.1', bugfixes: true, browserslistEnv: api.env('production') ? 'production' : 'dev', }, diff --git a/yarn.lock b/yarn.lock index b03565fd3c8c..5c14ae9c0ea4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1699,25 +1699,7 @@ dependencies: "@babel/plugin-syntax-jsx" "^7.17.12" -"@emotion/babel-plugin@^11.10.0": - version "11.10.2" - resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.2.tgz#879db80ba622b3f6076917a1e6f648b1c7d008c7" - integrity sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA== - dependencies: - "@babel/helper-module-imports" "^7.16.7" - "@babel/plugin-syntax-jsx" "^7.17.12" - "@babel/runtime" "^7.18.3" - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/serialize" "^1.1.0" - babel-plugin-macros "^3.1.0" - convert-source-map "^1.5.0" - escape-string-regexp "^4.0.0" - find-root "^1.1.0" - source-map "^0.5.7" - stylis "4.0.13" - -"@emotion/babel-plugin@^11.10.5": +"@emotion/babel-plugin@^11.10.0", "@emotion/babel-plugin@^11.10.5": version "11.10.5" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz#65fa6e1790ddc9e23cc22658a4c5dea423c55c3c" integrity sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA== @@ -1876,18 +1858,7 @@ "@emotion/utils" "0.11.3" csstype "^2.5.7" -"@emotion/serialize@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.0.tgz#b1f97b1011b09346a40e9796c37a3397b4ea8ea8" - integrity sha512-F1ZZZW51T/fx+wKbVlwsfchr5q97iW8brAnXmsskz4d0hVB4O3M/SiA3SaeH06x02lSNzkkQv+n3AX3kCXKSFA== - dependencies: - "@emotion/hash" "^0.9.0" - "@emotion/memoize" "^0.8.0" - "@emotion/unitless" "^0.8.0" - "@emotion/utils" "^1.2.0" - csstype "^3.0.2" - -"@emotion/serialize@^1.1.1": +"@emotion/serialize@^1.1.0", "@emotion/serialize@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.1.1.tgz#0595701b1902feded8a96d293b26be3f5c1a5cf0" integrity sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA== @@ -11235,10 +11206,10 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.9: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.4, core-js@^3.26.1, core-js@^3.6.5, core-js@^3.8.2, core-js@^3.8.3: - version "3.26.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.26.1.tgz#7a9816dabd9ee846c1c0fe0e8fcad68f3709134e" - integrity sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA== +core-js@^3.0.4, core-js@^3.27.0, core-js@^3.6.5, core-js@^3.8.2, core-js@^3.8.3: + version "3.27.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.27.1.tgz#23cc909b315a6bb4e418bf40a52758af2103ba46" + integrity sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww== core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -12957,22 +12928,7 @@ ejs@^3.1.8: dependencies: jake "^10.8.5" -elastic-apm-http-client@11.0.3, elastic-apm-http-client@^11.0.1: - version "11.0.3" - resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.0.3.tgz#1d357af449d66695ef10019c21efe6377ad8815e" - integrity sha512-y+P9ByvfxjZbnLejgGaCAnwEe+FWMVshoMmjeLEEEVlQTLiFUHy7vhYyCQVqgbZzQ6zpaGPqPU2woKglKW4RHw== - dependencies: - agentkeepalive "^4.2.1" - breadth-filter "^2.0.0" - end-of-stream "^1.4.4" - fast-safe-stringify "^2.0.7" - fast-stream-to-buffer "^1.0.0" - object-filter-sequence "^1.0.0" - readable-stream "^3.4.0" - semver "^6.3.0" - stream-chopper "^3.0.1" - -elastic-apm-http-client@11.0.4: +elastic-apm-http-client@11.0.4, elastic-apm-http-client@^11.0.1: version "11.0.4" resolved "https://registry.yarnpkg.com/elastic-apm-http-client/-/elastic-apm-http-client-11.0.4.tgz#3e44e56fa42235b1b16a33c6a7656cfde595f9ff" integrity sha512-449Qj/STi9hgnIk2KQ7719E7lpM3/i4Afs7NUhSOX8wV3sxn/+ItIHx9kKJthzhDDezxIfQcH83v83AF67GspQ== @@ -12987,45 +12943,7 @@ elastic-apm-http-client@11.0.4: semver "^6.3.0" stream-chopper "^3.0.1" -elastic-apm-node@^3.38.0: - version "3.40.1" - resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.40.1.tgz#ae3669d480fdacf62ace40d12a6f1a3c46b37940" - integrity sha512-vdyEZ7BPKJP2a1PkCsg350XXGZj03bwOiGrZdqgflocYxns5QwFbhvMKaVq7hWWWS8/sACesrLLELyQgdOpFsw== - dependencies: - "@elastic/ecs-pino-format" "^1.2.0" - "@opentelemetry/api" "^1.1.0" - after-all-results "^2.0.0" - async-cache "^1.1.0" - async-value-promise "^1.1.1" - basic-auth "^2.0.1" - cookie "^0.5.0" - core-util-is "^1.0.2" - elastic-apm-http-client "11.0.3" - end-of-stream "^1.4.4" - error-callsites "^2.0.4" - error-stack-parser "^2.0.6" - escape-string-regexp "^4.0.0" - fast-safe-stringify "^2.0.7" - http-headers "^3.0.2" - is-native "^1.0.1" - lru-cache "^6.0.0" - measured-reporting "^1.51.1" - monitor-event-loop-delay "^1.0.0" - object-filter-sequence "^1.0.0" - object-identity-map "^1.0.2" - original-url "^1.2.3" - pino "^6.11.2" - relative-microtime "^2.0.0" - require-in-the-middle "^5.2.0" - semver "^6.3.0" - set-cookie-serde "^1.0.0" - shallow-clone-shim "^2.0.0" - source-map "^0.8.0-beta.0" - sql-summary "^1.0.1" - traverse "^0.6.6" - unicode-byte-truncate "^1.0.0" - -elastic-apm-node@^3.41.0: +elastic-apm-node@^3.38.0, elastic-apm-node@^3.41.0: version "3.41.0" resolved "https://registry.yarnpkg.com/elastic-apm-node/-/elastic-apm-node-3.41.0.tgz#ff0b5ecf6d126a2eb312ecc936bd55bc3f1e3ce6" integrity sha512-cX745ryAwQCFEhkYttLrdjQDO0vCHspCbKg6bCguvBURZUHkLnZcP5z5m3R20dnGfTDXGg2q6nMi65iIVMCYFA== @@ -14330,18 +14248,7 @@ fast-glob@^2.2.6: merge2 "^1.2.3" micromatch "^3.1.10" -fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.2, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.2.7: +fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.2, fast-glob@^3.2.7, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== From cf5280de5c222c64b64b8c67cd55305e4c68b49c Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+dimaanj@users.noreply.github.com> Date: Tue, 3 Jan 2023 18:41:31 +0300 Subject: [PATCH 07/30] [Discover] Migrate `field_calculator.js` to typescript (#148187) ## Summary Closes #138114 This PR replaces `field_calculator.js` with `field_calculator.ts` --- .../discover_field_details.tsx | 12 +-- .../deprecated_stats/field_calculator.test.ts | 74 ++++++++++--------- ...ield_calculator.js => field_calculator.ts} | 69 +++++++++-------- .../sidebar/deprecated_stats/get_details.ts | 33 ++++----- .../sidebar/deprecated_stats/types.ts | 10 ++- 5 files changed, 109 insertions(+), 89 deletions(-) rename src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/{field_calculator.js => field_calculator.ts} (62%) diff --git a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx index 58db010c025c..d1f32ae8df8f 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx +++ b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/discover_field_details.tsx @@ -12,8 +12,8 @@ import { EuiText, EuiSpacer, EuiLink, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { DataViewField, DataView } from '@kbn/data-views-plugin/public'; import { DiscoverFieldBucket } from './discover_field_bucket'; -import { Bucket, FieldDetails } from './types'; -import { getDetails } from './get_details'; +import { Bucket } from './types'; +import { getDetails, isValidFieldDetails } from './get_details'; import { DataDocuments$ } from '../../../hooks/use_saved_search'; import { FetchStatus } from '../../../../types'; @@ -33,13 +33,13 @@ export function DiscoverFieldDetails({ dataView, onAddFilter, }: DiscoverFieldDetailsProps) { - const details: FieldDetails = useMemo(() => { + const details = useMemo(() => { const data = documents$.getValue(); const documents = data.fetchStatus === FetchStatus.COMPLETE ? data.result : undefined; return getDetails(field, documents, dataView); }, [field, documents$, dataView]); - if (!details?.error && !details?.buckets) { + if (!details) { return null; } @@ -52,8 +52,8 @@ export function DiscoverFieldDetails({ })}
- {details.error && {details.error}} - {!details.error && ( + {!isValidFieldDetails(details) && {details.error}} + {isValidFieldDetails(details) && ( <>
{details.buckets.map((bucket: Bucket, idx: number) => ( diff --git a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.test.ts b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.test.ts index f043d81baaf3..ef397acc63ab 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.test.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.test.ts @@ -9,10 +9,21 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { keys, clone, uniq, filter, map } from 'lodash'; import { getDataTableRecords } from '../../../../../__fixtures__/real_hits'; -import type { DataView } from '@kbn/data-views-plugin/public'; -// @ts-expect-error -import { fieldCalculator } from './field_calculator'; +import { fieldCalculator, FieldCountsParams } from './field_calculator'; import { stubLogstashDataView as dataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { FieldDetails, ValidFieldDetails } from './types'; +import { isValidFieldDetails } from './get_details'; + +const validateResults = ( + extensions: FieldDetails, + validate: (extensions: ValidFieldDetails) => void +) => { + if (isValidFieldDetails(extensions)) { + validate(extensions); + } else { + throw new Error('extensions is not valid'); + } +}; describe('fieldCalculator', function () { it('should have a _countMissing that counts nulls & undefineds in an array', function () { @@ -122,72 +133,67 @@ describe('fieldCalculator', function () { it('Should return an array of values for _source fields', function () { const extensions = fieldCalculator.getFieldValues( hits, - dataView.fields.getByName('extension'), - dataView + dataView.fields.getByName('extension')! ); expect(extensions).toBeInstanceOf(Array); - expect( - filter(extensions, function (v) { - return v === 'html'; - }).length - ).toBe(8); + expect(filter(extensions, (v) => v === 'html').length).toBe(8); expect(uniq(clone(extensions)).sort()).toEqual(['gif', 'html', 'php', 'png']); }); it('Should return an array of values for core meta fields', function () { - const types = fieldCalculator.getFieldValues( - hits, - dataView.fields.getByName('_id'), - dataView - ); + const types = fieldCalculator.getFieldValues(hits, dataView.fields.getByName('_id')!); expect(types).toBeInstanceOf(Array); expect(types.length).toBe(20); }); }); describe('getFieldValueCounts', function () { - let params: { hits: any; field: any; count: number; dataView: DataView }; + let params: FieldCountsParams; beforeEach(function () { params = { hits: getDataTableRecords(dataView), - field: dataView.fields.getByName('extension'), + field: dataView.fields.getByName('extension')!, count: 3, dataView, }; }); it('counts the top 3 values', function () { - const extensions = fieldCalculator.getFieldValueCounts(params); - expect(extensions).toBeInstanceOf(Object); - expect(extensions.buckets).toBeInstanceOf(Array); - expect(extensions.buckets.length).toBe(3); - expect(map(extensions.buckets, 'value')).toEqual(['html', 'php', 'gif']); - expect(extensions.error).toBe(undefined); + validateResults(fieldCalculator.getFieldValueCounts(params), (extensions) => { + expect(extensions).toBeInstanceOf(Object); + expect(extensions.buckets).toBeInstanceOf(Array); + expect(extensions.buckets.length).toBe(3); + expect(map(extensions.buckets, 'value')).toEqual(['html', 'php', 'gif']); + }); }); it('fails to analyze geo and attachment types', function () { - params.field = dataView.fields.getByName('point'); - expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); + params.field = dataView.fields.getByName('point')!; + expect(isValidFieldDetails(fieldCalculator.getFieldValueCounts(params))).toBeFalsy(); - params.field = dataView.fields.getByName('area'); - expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); + params.field = dataView.fields.getByName('area')!; + expect(isValidFieldDetails(fieldCalculator.getFieldValueCounts(params))).toBeFalsy(); - params.field = dataView.fields.getByName('request_body'); - expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); + params.field = dataView.fields.getByName('request_body')!; + expect(isValidFieldDetails(fieldCalculator.getFieldValueCounts(params))).toBeFalsy(); }); it('fails to analyze fields that are in the mapping, but not the hits', function () { - params.field = dataView.fields.getByName('ip'); - expect(fieldCalculator.getFieldValueCounts(params).error).not.toBe(undefined); + params.field = dataView.fields.getByName('ip')!; + expect(isValidFieldDetails(fieldCalculator.getFieldValueCounts(params))).toBeFalsy(); }); it('counts the total hits', function () { - expect(fieldCalculator.getFieldValueCounts(params).total).toBe(params.hits.length); + validateResults(fieldCalculator.getFieldValueCounts(params), (extensions) => { + expect(extensions.total).toBe(params.hits.length); + }); }); it('counts the hits the field exists in', function () { - params.field = dataView.fields.getByName('phpmemory'); - expect(fieldCalculator.getFieldValueCounts(params).exists).toBe(5); + params.field = dataView.fields.getByName('phpmemory')!; + validateResults(fieldCalculator.getFieldValueCounts(params), (extensions) => { + expect(extensions.exists).toBe(5); + }); }); }); }); diff --git a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.js b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.ts similarity index 62% rename from src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.js rename to src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.ts index 68a774f4134a..eff4ff793b2e 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.js +++ b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/field_calculator.ts @@ -8,15 +8,27 @@ import { map, sortBy, without, each, defaults, isObject } from 'lodash'; import { i18n } from '@kbn/i18n'; +import type { DataViewField, DataView } from '@kbn/data-views-plugin/common'; +import type { DataTableRecord } from '../../../../../types'; +import { Bucket, FieldDetails } from './types'; -function getFieldValues(hits, field) { - const name = field.name; - return map(hits, function (hit) { - return hit.flattened[name]; - }); +export interface FieldCountsParams { + hits: DataTableRecord[]; + field: DataViewField; + dataView: DataView; + count?: number; + grouped?: boolean; +} + +interface FieldCountsBucket { + count: number; + value: string; } -function getFieldValueCounts(params) { +const getFieldValues = (hits: DataTableRecord[], field: DataViewField): unknown[] => + map(hits, (hit) => hit.flattened[field.name]); + +const getFieldValueCounts = (params: FieldCountsParams): FieldDetails => { params = defaults(params, { count: 5, grouped: false, @@ -38,18 +50,19 @@ function getFieldValueCounts(params) { } const allValues = getFieldValues(params.hits, params.field); - let counts; const missing = _countMissing(allValues); try { const groups = _groupValues(allValues, params); - counts = map(sortBy(groups, 'count').reverse().slice(0, params.count), function (bucket) { - return { + const counts: Bucket[] = sortBy(groups, 'count') + .reverse() + .slice(0, params.count) + .map((bucket: FieldCountsBucket) => ({ value: bucket.value, - count: bucket.count, - percent: ((bucket.count / (params.hits.length - missing)) * 100).toFixed(1), - }; - }); + count: bucket.count as number, + percent: Number(((bucket.count / (params.hits.length - missing)) * 100).toFixed(1)), + display: params.dataView.getFormatterForField(params.field).convert(bucket.value), + })); if (params.hits.length - missing === 0) { return { @@ -69,24 +82,22 @@ function getFieldValueCounts(params) { return { total: params.hits.length, exists: params.hits.length - missing, - missing: missing, + missing, buckets: counts, }; } catch (e) { return { error: e.message }; } -} +}; // returns a count of fields in the array that are undefined or null -function _countMissing(array) { - return array.length - without(array, undefined, null).length; -} +const _countMissing = (array: unknown[]) => array.length - without(array, undefined, null).length; -function _groupValues(allValues, params) { - const groups = {}; +const _groupValues = (allValues: unknown[], params: FieldCountsParams) => { + const groups: Record = {}; let k; - allValues.forEach(function (value) { + allValues.forEach((value: unknown) => { if (isObject(value) && !Array.isArray(value)) { throw new Error( i18n.translate( @@ -104,12 +115,12 @@ function _groupValues(allValues, params) { k = value == null ? undefined : [value]; } - each(k, function (key) { + each(k, (key: string) => { if (groups.hasOwnProperty(key)) { - groups[key].count++; + (groups[key] as FieldCountsBucket).count++; } else { groups[key] = { - value: params.grouped ? value : key, + value: params.grouped ? (value as string) : key, count: 1, }; } @@ -117,11 +128,11 @@ function _groupValues(allValues, params) { }); return groups; -} +}; export const fieldCalculator = { - _groupValues: _groupValues, - _countMissing: _countMissing, - getFieldValues: getFieldValues, - getFieldValueCounts: getFieldValueCounts, + _groupValues, + _countMissing, + getFieldValues, + getFieldValueCounts, }; diff --git a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/get_details.ts b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/get_details.ts index cc9f56b73fee..d62d3e10dd34 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/get_details.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/get_details.ts @@ -7,30 +7,27 @@ */ import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; -// @ts-expect-error import { fieldCalculator } from './field_calculator'; import { DataTableRecord } from '../../../../../types'; +import { ErrorFieldDetails, FieldDetails, ValidFieldDetails } from './types'; + +export const isValidFieldDetails = (details: FieldDetails): details is ValidFieldDetails => + !(details as ErrorFieldDetails).error; export function getDetails( field: DataViewField, hits: DataTableRecord[] | undefined, - dataView?: DataView + dataView: DataView ) { - if (!dataView || !hits) { - return {}; - } - const details = { - ...fieldCalculator.getFieldValueCounts({ - hits, - field, - count: 5, - grouped: false, - }), - }; - if (details.buckets) { - for (const bucket of details.buckets) { - bucket.display = dataView.getFormatterForField(field).convert(bucket.value); - } + if (!hits) { + return undefined; } - return details; + + return fieldCalculator.getFieldValueCounts({ + hits, + field, + count: 5, + grouped: false, + dataView, + }); } diff --git a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/types.ts b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/types.ts index 1f7d40418fe7..ba308d8e14bf 100644 --- a/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/types.ts +++ b/src/plugins/discover/public/application/main/components/sidebar/deprecated_stats/types.ts @@ -6,13 +6,19 @@ * Side Public License, v 1. */ -export interface FieldDetails { - error: string; +export interface ValidFieldDetails { exists: number; total: number; + missing: number; buckets: Bucket[]; } +export interface ErrorFieldDetails { + error: string; +} + +export type FieldDetails = ValidFieldDetails | ErrorFieldDetails; + export interface Bucket { display: string; value: string; From 0c8c4d7d0a3e9516d6764fbec3ac587926bcf9a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 3 Jan 2023 10:45:37 -0500 Subject: [PATCH 08/30] [Actions] Remove duplicated `ActionResult` type from `common` folder (#148001) Resolves https://github.com/elastic/kibana/issues/132412. In this PR, I'm removing `ActionResult` type from the `/common` folder as it is an old copy of the `ActionResult` used and provided by the `/server` folder. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/actions/common/types.ts | 10 ---------- .../monitoring/server/routes/api/v1/alerts/enable.ts | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/x-pack/plugins/actions/common/types.ts b/x-pack/plugins/actions/common/types.ts index 052ebc108077..fe52e1db5b28 100644 --- a/x-pack/plugins/actions/common/types.ts +++ b/x-pack/plugins/actions/common/types.ts @@ -34,16 +34,6 @@ export interface ValidatedEmail { reason?: InvalidEmailReason; } -export interface ActionResult { - id: string; - actionTypeId: string; - name: string; - // This will have to remain `any` until we can extend Action Executors with generics - // eslint-disable-next-line @typescript-eslint/no-explicit-any - config: Record; - isPreconfigured: boolean; -} - // the result returned from an action type executor function const ActionTypeExecutorResultStatusValues = ['ok', 'error'] as const; type ActionTypeExecutorResultStatus = typeof ActionTypeExecutorResultStatusValues[number]; diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts index 49ce33b0578a..18976b1ca26c 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ActionResult } from '@kbn/actions-plugin/common'; +import { ActionResult } from '@kbn/actions-plugin/server'; import { RuleTypeParams, SanitizedRule } from '@kbn/alerting-plugin/common'; import { ALERT_ACTION_TYPE_LOG } from '../../../../../common/constants'; import { AlertsFactory } from '../../../../alerts'; From 9f3d19fe1485e4ba37e0452735a8962577a02157 Mon Sep 17 00:00:00 2001 From: Adam Demjen Date: Tue, 3 Jan 2023 11:58:02 -0400 Subject: [PATCH 09/30] Fix typo in "Behavioral Analytics" nav labels (#148249) The left side nav label for Behavioral Analytics has a typo. This PR fixes the label, the test and the I18N default value of the same. --- x-pack/plugins/enterprise_search/common/constants.ts | 2 +- .../public/applications/shared/layout/nav.test.tsx | 4 ++-- .../public/applications/shared/layout/nav.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index 7ce736360524..425482890a36 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -44,7 +44,7 @@ export const ENTERPRISE_SEARCH_CONTENT_PLUGIN = { export const ANALYTICS_PLUGIN = { ID: 'enterpriseSearchAnalytics', NAME: i18n.translate('xpack.enterpriseSearch.analytics.productName', { - defaultMessage: 'Behavorial Analytics', + defaultMessage: 'Behavioral Analytics', }), DESCRIPTION: i18n.translate('xpack.enterpriseSearch.analytics.productDescription', { defaultMessage: diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx index 43bf011c420d..06f31ba047df 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.test.tsx @@ -62,7 +62,7 @@ describe('useEnterpriseSearchContentNav', () => { name: 'Collections', }, ], - name: 'Behavorial Analytics', + name: 'Behavioral Analytics', }, { id: 'search', @@ -265,7 +265,7 @@ describe('useEnterpriseSearchContentNav Engines feature flag', () => { name: 'Collections', }, ], - name: 'Behavorial Analytics', + name: 'Behavioral Analytics', }, { id: 'standaloneExperiences', diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx index ad61d22ccfcb..83f5cbca3ca4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/nav.tsx @@ -91,7 +91,7 @@ export const useEnterpriseSearchNav = () => { }, ], name: i18n.translate('xpack.enterpriseSearch.nav.analyticsTitle', { - defaultMessage: 'Behavorial Analytics', + defaultMessage: 'Behavioral Analytics', }), }, { From cb6a3bd0524847256959bd29192455816dcb07c1 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 3 Jan 2023 17:06:31 +0100 Subject: [PATCH 10/30] [Synthetics] Fix overview status query (#148190) --- .../private_locations/manage_locations.ts | 1 + .../overview/overview/metric_item.tsx | 2 +- .../status_rule/status_rule_executor.ts | 45 +++- .../telemetry/kibana_telemetry_adapter.ts | 4 +- .../lib/alerts/duration_anomaly.ts | 8 +- .../legacy_uptime/lib/alerts/status_check.ts | 8 +- .../server/legacy_uptime/lib/alerts/tls.ts | 8 +- .../legacy_uptime/lib/alerts/tls_legacy.ts | 8 +- .../server/legacy_uptime/lib/lib.ts | 242 +++++++++--------- .../lib/requests/get_index_status.ts | 2 +- .../lib/requests/test_helpers.ts | 7 +- .../routes/uptime_route_wrapper.ts | 16 +- .../server/queries/query_monitor_status.ts | 91 ++++--- .../routes/status/current_status.test.ts | 31 ++- .../server/routes/status/current_status.ts | 156 +++++++---- .../synthetics_monitor/get_all_monitors.ts | 17 +- .../server/synthetics_route_wrapper.ts | 25 +- 17 files changed, 405 insertions(+), 266 deletions(-) diff --git a/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts b/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts index a28a29be15f8..9a1e1d1e2772 100644 --- a/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts +++ b/x-pack/plugins/synthetics/e2e/journeys/uptime/private_locations/manage_locations.ts @@ -75,6 +75,7 @@ journey('ManagePrivateLocation', async ({ page, params: { kibanaUrl } }) => { const addAgentPolicy = async (name: string) => { await page.click('[placeholder="Choose a name"]'); await page.fill('[placeholder="Choose a name"]', name); + await page.click('text=Collect system logs and metrics'); await page.click('div[role="dialog"] button:has-text("Create agent policy")'); }; }); diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx index 82b7cc6be2dc..5fca2b11875e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx @@ -52,7 +52,7 @@ export const MetricItem = ({ const [isMouseOver, setIsMouseOver] = useState(false); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const locationName = useLocationName({ locationId: monitor.location?.id }); - const status = useStatusByLocationOverview(monitor.configId, locationName); + const status = useStatusByLocationOverview(monitor.id, locationName); const theme = useTheme(); return ( diff --git a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts index e7119d1912f4..f750fa2f5b47 100644 --- a/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts +++ b/x-pack/plugins/synthetics/server/alert_rules/status_rule/status_rule_executor.ts @@ -10,12 +10,13 @@ import { SavedObjectsFindResult, } from '@kbn/core-saved-objects-api-server'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { resolveMissingLabels } from '../../routes/status/current_status'; import { AlertConfigKey } from '../../../common/constants/monitor_management'; import { getAllLocations } from '../../synthetics_service/get_all_locations'; import { getAllMonitors } from '../../saved_objects/synthetics_monitor/get_all_monitors'; import { GetMonitorDownStatusMessageParams } from '../../legacy_uptime/lib/requests/get_monitor_status'; import { queryMonitorStatus } from '../../queries/query_monitor_status'; -import { createUptimeESClient, UptimeEsClient } from '../../legacy_uptime/lib/lib'; +import { UptimeEsClient } from '../../legacy_uptime/lib/lib'; import { StatusRuleParams } from '../../../common/rules/status_rule'; import { ConfigKey, @@ -58,10 +59,7 @@ export class StatusRuleExecutor { this.previousStartedAt = previousStartedAt; this.params = p; this.soClient = soClient; - this.esClient = createUptimeESClient({ - esClient: scopedClient, - savedObjectsClient: this.soClient, - }); + this.esClient = new UptimeEsClient(this.soClient, scopedClient); this.server = server; this.syntheticsMonitorClient = syntheticsMonitorClient; } @@ -88,13 +86,14 @@ export class StatusRuleExecutor { async getMonitors() { await this.getAllLocationNames(); - this.monitors = await getAllMonitors( - this.soClient, - `attributes.${AlertConfigKey.STATUS_ENABLED}: true` - ); + this.monitors = await getAllMonitors({ + soClient: this.soClient, + search: `attributes.${AlertConfigKey.STATUS_ENABLED}: true`, + }); const allIds: string[] = []; const enabledIds: string[] = []; - let maxLocations = 1; + let listOfLocationsSet = new Set(); + const missingLabelLocations = new Set(); this.monitors.forEach((monitor) => { const attrs = monitor.attributes; @@ -103,21 +102,39 @@ export class StatusRuleExecutor { enabledIds.push(attrs[ConfigKey.MONITOR_QUERY_ID]); } - maxLocations = Math.max(maxLocations, attrs[ConfigKey.LOCATIONS].length); + listOfLocationsSet = new Set([ + ...listOfLocationsSet, + ...(attrs[ConfigKey.LOCATIONS] + .filter((loc) => { + if (!loc.label) { + missingLabelLocations.add(loc.id); + } + return loc.label; + }) + .map((location) => location.label) as string[]), + ]); }); - return { enabledIds, maxLocations, allIds }; + const { listOfLocations } = await resolveMissingLabels( + this.server, + this.soClient, + this.syntheticsMonitorClient, + listOfLocationsSet, + missingLabelLocations + ); + + return { enabledIds, listOfLocations, allIds }; } async getDownChecks( prevDownConfigs: OverviewStatus['downConfigs'] = {} ): Promise { - const { maxLocations, enabledIds } = await this.getMonitors(); + const { listOfLocations, enabledIds } = await this.getMonitors(); if (enabledIds.length > 0) { const currentStatus = await queryMonitorStatus( this.esClient, - maxLocations, + [...listOfLocations], { to: 'now', from: this.previousStartedAt?.toISOString() ?? 'now-1m' }, enabledIds ); diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/telemetry/kibana_telemetry_adapter.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/telemetry/kibana_telemetry_adapter.ts index cd852fc537d6..2fa270c88cef 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/telemetry/kibana_telemetry_adapter.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/adapters/telemetry/kibana_telemetry_adapter.ts @@ -10,7 +10,7 @@ import { SavedObjectsClientContract } from '@kbn/core/server'; import { CollectorFetchContext, UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { PageViewParams, UptimeTelemetry, Usage } from './types'; import { savedObjectsAdapter } from '../../saved_objects/saved_objects'; -import { UptimeEsClient, createUptimeESClient } from '../../lib'; +import { UptimeEsClient } from '../../lib'; import { createEsQuery } from '../../../../../common/utils/es_search'; interface UptimeTelemetryCollector { @@ -157,7 +157,7 @@ export class KibanaTelemetryAdapter { fetch: async ({ esClient }: CollectorFetchContext) => { const savedObjectsClient = getSavedObjectsClient()!; if (savedObjectsClient) { - const uptimeEsClient = createUptimeESClient({ esClient, savedObjectsClient }); + const uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient); await this.countNoOfUniqueMonitorAndLocations(uptimeEsClient, savedObjectsClient); await this.countNoOfUniqueFleetManagedMonitors(uptimeEsClient); } diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts index ed161a4d1eff..dc780a4d2ec3 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/duration_anomaly.ts @@ -15,6 +15,7 @@ import { import { ActionGroupIdsOf } from '@kbn/alerting-plugin/common'; import { AnomaliesTableRecord } from '@kbn/ml-plugin/common/types/anomalies'; import { getSeverityType } from '@kbn/ml-plugin/common/util/anomaly_utils'; +import { UptimeEsClient } from '../lib'; import { updateState, generateAlertMessage, @@ -31,7 +32,6 @@ import { getMLJobId } from '../../../../common/lib'; import { DurationAnomalyTranslations as CommonDurationAnomalyTranslations } from '../../../../common/translations'; import { getMonitorRouteFromMonitorId } from '../../../../common/utils/get_monitor_url'; -import { createUptimeESClient } from '../lib'; import { ALERT_REASON_MSG, ACTION_VARIABLES, VIEW_IN_APP_URL } from './action_variables'; export type ActionGroupIds = ActionGroupIdsOf; @@ -125,10 +125,10 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory state, startedAt, }) { - const uptimeEsClient = createUptimeESClient({ - esClient: scopedClusterClient.asCurrentUser, + const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - }); + scopedClusterClient.asCurrentUser + ); const { basePath } = server; const { anomalies } = diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts index 90aeb404aeae..49593c95727a 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/status_check.ts @@ -42,7 +42,7 @@ import { } from '../requests/get_monitor_status'; import { UNNAMED_LOCATION } from '../../../../common/constants'; import { getUptimeIndexPattern, IndexPatternTitleAndFields } from '../requests/get_index_pattern'; -import { UMServerLibs, UptimeEsClient, createUptimeESClient } from '../lib'; +import { UMServerLibs, UptimeEsClient } from '../lib'; import { ACTION_VARIABLES, ALERT_DETAILS_URL, @@ -366,10 +366,10 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( timerangeUnit, } = rawParams; const { basePath } = server; - const uptimeEsClient = createUptimeESClient({ - esClient: scopedClusterClient.asCurrentUser, + const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - }); + scopedClusterClient.asCurrentUser + ); const filterString = await formatFilterString(uptimeEsClient, filters, search, libs); const timespanInterval = `${String(timerangeCount)}${timerangeUnit}`; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts index e90cdf280c4c..44b191e72023 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls.ts @@ -22,7 +22,7 @@ import { commonStateTranslations, tlsTranslations } from './translations'; import { TlsTranslations } from '../../../../common/translations'; import { savedObjectsAdapter } from '../saved_objects/saved_objects'; -import { createUptimeESClient } from '../lib'; +import { UptimeEsClient } from '../lib'; import { ACTION_VARIABLES, ALERT_DETAILS_URL } from './action_variables'; export type ActionGroupIds = ActionGroupIdsOf; @@ -144,10 +144,10 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = ( const { basePath } = _server; const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); - const uptimeEsClient = createUptimeESClient({ - esClient: scopedClusterClient.asCurrentUser, + const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - }); + scopedClusterClient.asCurrentUser + ); const { certs, total }: CertResult = await libs.requests.getCerts({ uptimeEsClient, diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls_legacy.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls_legacy.ts index 6e333093eb20..732b6ca45bc9 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls_legacy.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/alerts/tls_legacy.ts @@ -18,7 +18,7 @@ import { Cert, CertResult } from '../../../../common/runtime_types'; import { commonStateTranslations, tlsTranslations } from './translations'; import { savedObjectsAdapter } from '../saved_objects/saved_objects'; -import { createUptimeESClient } from '../lib'; +import { UptimeEsClient } from '../lib'; import { DEFAULT_FROM, DEFAULT_SIZE, @@ -115,10 +115,10 @@ export const tlsLegacyAlertFactory: UptimeAlertTypeFactory = (_s async executor({ services: { alertFactory, scopedClusterClient, savedObjectsClient }, state }) { const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); - const uptimeEsClient = createUptimeESClient({ - esClient: scopedClusterClient.asCurrentUser, + const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - }); + scopedClusterClient.asCurrentUser + ); const { certs, total }: CertResult = await libs.requests.getCerts({ uptimeEsClient, from: DEFAULT_FROM, diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts index 12c67e5d346f..de76507c330d 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/lib.ts @@ -46,132 +46,144 @@ export interface CountResponse { indices: string; } -export type UptimeEsClient = ReturnType; - -export function createUptimeESClient({ - isDev, - uiSettings, - esClient, - request, - savedObjectsClient, -}: { - isDev?: boolean; - uiSettings?: CoreRequestHandlerContext['uiSettings']; - esClient: ElasticsearchClient; +export class UptimeEsClient { + isDev: boolean; request?: KibanaRequest; + baseESClient: ElasticsearchClient; + heartbeatIndices: string; + isInspectorEnabled: boolean | undefined; + inspectableEsQueries: InspectResponse = []; + uiSettings?: CoreRequestHandlerContext['uiSettings']; savedObjectsClient: SavedObjectsClientContract; -}) { - const initSettings = async (self: UptimeEsClient) => { + + constructor( + savedObjectsClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + isDev: boolean = false, + uiSettings?: CoreRequestHandlerContext['uiSettings'], + request?: KibanaRequest + ) { + this.uiSettings = uiSettings; + this.baseESClient = esClient; + this.isInspectorEnabled = undefined; + this.savedObjectsClient = savedObjectsClient; + this.request = request; + this.heartbeatIndices = ''; + this.isDev = isDev; + this.inspectableEsQueries = []; + } + + async initSettings() { + const self = this; if (!self.heartbeatIndices) { const [isInspectorEnabled, dynamicSettings] = await Promise.all([ - getInspectEnabled(uiSettings), - savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient), + getInspectEnabled(self.uiSettings), + savedObjectsAdapter.getUptimeDynamicSettings(self.savedObjectsClient), ]); self.heartbeatIndices = dynamicSettings?.heartbeatIndices || ''; self.isInspectorEnabled = isInspectorEnabled; + } + } + + async search( + params: TParams, + operationName?: string, + index?: string + ): Promise<{ body: ESSearchResponse }> { + let res: any; + let esError: any; + + await this.initSettings(); + + const esParams = { index: index ?? this.heartbeatIndices, ...params }; + const startTime = process.hrtime(); + + const startTimeNow = Date.now(); - if (self.isInspectorEnabled || isDev) { - self.inspectableEsQueriesMap.set(request!, []); - } + let esRequestStatus: RequestStatus = RequestStatus.PENDING; + + try { + res = await this.baseESClient.search(esParams, { meta: true }); + esRequestStatus = RequestStatus.OK; + } catch (e) { + esError = e; + esRequestStatus = RequestStatus.ERROR; + } + if (this.request) { + this.inspectableEsQueries.push( + getInspectResponse({ + esError, + esRequestParams: esParams, + esRequestStatus, + esResponse: res?.body, + kibanaRequest: this.request, + operationName: operationName ?? '', + startTime: startTimeNow, + }) + ); } - }; - return { - inspectableEsQueriesMap: new WeakMap(), - baseESClient: esClient, - heartbeatIndices: '', - isInspectorEnabled: undefined as boolean | undefined, - async search( - params: TParams, - operationName?: string, - index?: string - ): Promise<{ body: ESSearchResponse }> { - let res: any; - let esError: any; - - await initSettings(this); - - const esParams = { index: index ?? this.heartbeatIndices, ...params }; - const startTime = process.hrtime(); - - const startTimeNow = Date.now(); - - let esRequestStatus: RequestStatus = RequestStatus.PENDING; - - try { - res = await esClient.search(esParams, { meta: true }); - esRequestStatus = RequestStatus.OK; - } catch (e) { - esError = e; - esRequestStatus = RequestStatus.ERROR; - } - - const inspectableEsQueries = this.inspectableEsQueriesMap.get(request!); - - if (inspectableEsQueries) { - inspectableEsQueries.push( - getInspectResponse({ - esError, - esRequestParams: esParams, - esRequestStatus, - esResponse: res?.body, - kibanaRequest: request!, - operationName: operationName ?? '', - startTime: startTimeNow, - }) - ); - if (request && this.isInspectorEnabled) { - debugESCall({ startTime, request, esError, operationName: 'search', params: esParams }); - } - } - - if (esError) { - throw esError; - } - - return res; - }, - async count(params: TParams): Promise { - let res: any; - let esError: any; - - await initSettings(this); - - const esParams = { index: this.heartbeatIndices, ...params }; - const startTime = process.hrtime(); - - try { - res = await esClient.count(esParams, { meta: true }); - } catch (e) { - esError = e; - } - const inspectableEsQueries = this.inspectableEsQueriesMap.get(request!); - - if (inspectableEsQueries && request && this.isInspectorEnabled) { - debugESCall({ startTime, request, esError, operationName: 'count', params: esParams }); - } - - if (esError) { - throw esError; - } - - return { result: res, indices: this.heartbeatIndices }; - }, - getSavedObjectsClient() { - return savedObjectsClient; - }, - - getInspectData(path: string) { - const isInspectorEnabled = - (this.isInspectorEnabled || isDev) && path !== API_URLS.DYNAMIC_SETTINGS; - - if (isInspectorEnabled) { - return { _inspect: this.inspectableEsQueriesMap.get(request!) }; - } - return {}; - }, - }; + if (this.isInspectorEnabled && this.request) { + debugESCall({ + startTime, + request: this.request, + esError, + operationName: 'search', + params: esParams, + }); + } + + if (esError) { + throw esError; + } + + return res; + } + async count(params: TParams): Promise { + let res: any; + let esError: any; + + await this.initSettings(); + + const esParams = { index: this.heartbeatIndices, ...params }; + const startTime = process.hrtime(); + + try { + res = await this.baseESClient.count(esParams, { meta: true }); + } catch (e) { + esError = e; + } + + if (this.isInspectorEnabled && this.request) { + debugESCall({ + startTime, + request: this.request, + esError, + operationName: 'count', + params: esParams, + }); + } + + if (esError) { + throw esError; + } + + return { result: res, indices: this.heartbeatIndices }; + } + getSavedObjectsClient() { + return this.savedObjectsClient; + } + + getInspectData(path: string) { + const isInspectorEnabled = + (this.isInspectorEnabled || this.isDev) && path !== API_URLS.DYNAMIC_SETTINGS; + + if (isInspectorEnabled) { + return { _inspect: this.inspectableEsQueries }; + } + return {}; + } } function getInspectEnabled(uiSettings?: CoreRequestHandlerContext['uiSettings']) { diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts index 283aa4c04827..0c16f1459a56 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/get_index_status.ts @@ -25,7 +25,7 @@ export const getIndexStatus: UMElasticsearchQueryFn<{}, StatesIndexStatus> = asy indexExists: total > 0, }; } catch (e) { - if (e.meta.statusCode === 404) { + if (e.meta?.statusCode === 404) { // we don't throw an error for index not found return { indices: '', diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/test_helpers.ts b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/test_helpers.ts index b33bc8041902..e2a9fc4d7c60 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/test_helpers.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/lib/requests/test_helpers.ts @@ -9,7 +9,7 @@ import { AggregationsAggregate } from '@elastic/elasticsearch/lib/api/typesWithB import type { ElasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; -import { createUptimeESClient, UptimeEsClient } from '../lib'; +import { UptimeEsClient } from '../lib'; export interface MultiPageCriteria { after_key?: K; @@ -71,10 +71,7 @@ export const getUptimeESMockClient = ( return { esClient: esClientMock || esClient, - uptimeEsClient: createUptimeESClient({ - esClient: esClientMock || esClient, - savedObjectsClient, - }), + uptimeEsClient: new UptimeEsClient(savedObjectsClient, esClientMock || esClient), }; }; diff --git a/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts b/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts index 854b03492adf..b5a025c885dc 100644 --- a/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts +++ b/x-pack/plugins/synthetics/server/legacy_uptime/routes/uptime_route_wrapper.ts @@ -7,7 +7,7 @@ import { KibanaResponse } from '@kbn/core-http-router-server-internal'; import { UMKibanaRouteWrapper } from './types'; -import { createUptimeESClient, isTestUser } from '../lib/lib'; +import { isTestUser, UptimeEsClient } from '../lib/lib'; export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute, server) => ({ ...uptimeRoute, @@ -20,13 +20,13 @@ export const uptimeRouteWrapper: UMKibanaRouteWrapper = (uptimeRoute, server) => server.authSavedObjectsClient = coreContext.savedObjects.client; - const uptimeEsClient = createUptimeESClient({ - request, - isDev: server.isDev && !isTestUser(server), - savedObjectsClient: coreContext.savedObjects.client, - esClient: esClient.asCurrentUser, - uiSettings: coreContext.uiSettings, - }); + const uptimeEsClient = new UptimeEsClient( + coreContext.savedObjects.client, + esClient.asCurrentUser, + Boolean(server.isDev && !isTestUser(server)), + coreContext.uiSettings, + request + ); server.uptimeEsClient = uptimeEsClient; diff --git a/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts b/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts index 844b0eaf9391..ace8846e0e12 100644 --- a/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts +++ b/x-pack/plugins/synthetics/server/queries/query_monitor_status.ts @@ -5,7 +5,7 @@ * 2.0. */ -import * as estypes from '@elastic/elasticsearch/lib/api/types'; +import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { SUMMARY_FILTER } from '../../common/constants/client_defaults'; import { UptimeEsClient } from '../legacy_uptime/lib/lib'; import { OverviewStatus, OverviewStatusMetaData, Ping } from '../../common/runtime_types'; @@ -14,15 +14,16 @@ const DEFAULT_MAX_ES_BUCKET_SIZE = 10000; export async function queryMonitorStatus( esClient: UptimeEsClient, - maxLocations: number, + listOfLocations: string[], range: { from: string | number; to: string }, - ids: string[] + ids: string[], + monitorLocationsMap?: Record ): Promise> { - const idSize = Math.trunc(DEFAULT_MAX_ES_BUCKET_SIZE / maxLocations); + const idSize = Math.trunc(DEFAULT_MAX_ES_BUCKET_SIZE / listOfLocations.length || 1); const pageCount = Math.ceil(ids.length / idSize); const promises: Array> = []; for (let i = 0; i < pageCount; i++) { - const params: estypes.SearchRequest = { + const params: SearchRequest = { size: 0, query: { bool: { @@ -43,6 +44,15 @@ export async function queryMonitorStatus( 'monitor.id': (ids as string[]).slice(i * idSize, i * idSize + idSize), }, }, + ...(listOfLocations.length > 0 + ? [ + { + terms: { + 'observer.geo.name': listOfLocations, + }, + }, + ] + : []), ], }, }, @@ -56,7 +66,7 @@ export async function queryMonitorStatus( location: { terms: { field: 'observer.geo.name', - size: maxLocations, + size: listOfLocations.length || 100, }, aggs: { status: { @@ -90,43 +100,52 @@ export async function queryMonitorStatus( }, }; - promises.push(esClient.baseESClient.search(params)); + promises.push(esClient.search({ body: params }, 'getCurrentStatusOverview' + i)); } let up = 0; let down = 0; const upConfigs: Record = {}; const downConfigs: Record = {}; for await (const response of promises) { - response.aggregations?.id.buckets.forEach(({ location }: { key: string; location: any }) => { - location.buckets.forEach(({ status }: { key: string; status: any }) => { - const ping = status.hits.hits[0]._source as Ping; + response.body.aggregations?.id.buckets.forEach( + ({ location, key: queryId }: { location: any; key: string }) => { + location.buckets.forEach(({ status }: { key: string; status: any }) => { + const ping = status.hits.hits[0]._source as Ping; + + const locationName = ping.observer?.geo?.name!; + + const monLocations = monitorLocationsMap?.[queryId]; + if (monLocations && !monLocations.includes(locationName)) { + // discard any locations that are not in the monitorLocationsMap for the given monitor + return; + } - const downCount = ping.summary?.down ?? 0; - const upCount = ping.summary?.up ?? 0; - const configId = ping.config_id!; - const monitorQueryId = ping.monitor.id; - const locationName = ping.observer?.geo?.name!; - if (upCount > 0) { - up += 1; - upConfigs[`${configId}-${locationName}`] = { - ping, - configId, - monitorQueryId, - location: locationName, - status: 'up', - }; - } else if (downCount > 0) { - down += 1; - downConfigs[`${configId}-${locationName}`] = { - ping, - configId, - monitorQueryId, - location: locationName, - status: 'down', - }; - } - }); - }); + const downCount = ping.summary?.down ?? 0; + const upCount = ping.summary?.up ?? 0; + const configId = ping.config_id!; + const monitorQueryId = ping.monitor.id; + if (upCount > 0) { + up += 1; + upConfigs[`${configId}-${locationName}`] = { + ping, + configId, + monitorQueryId, + location: locationName, + status: 'up', + }; + } else if (downCount > 0) { + down += 1; + downConfigs[`${configId}-${locationName}`] = { + ping, + configId, + monitorQueryId, + location: locationName, + status: 'down', + }; + } + }); + } + ); } return { up, down, upConfigs, downConfigs, enabledIds: ids }; } diff --git a/x-pack/plugins/synthetics/server/routes/status/current_status.test.ts b/x-pack/plugins/synthetics/server/routes/status/current_status.test.ts index 09deb21539f4..39184508bf6c 100644 --- a/x-pack/plugins/synthetics/server/routes/status/current_status.test.ts +++ b/x-pack/plugins/synthetics/server/routes/status/current_status.test.ts @@ -8,6 +8,7 @@ import { getUptimeESMockClient } from '../../legacy_uptime/lib/requests/test_helpers'; import { periodToMs } from './current_status'; import { queryMonitorStatus } from '../../queries/query_monitor_status'; +import times from 'lodash/times'; jest.mock('../common', () => ({ getMonitors: jest.fn().mockReturnValue({ @@ -166,7 +167,12 @@ describe('current status route', () => { ]) ); expect( - await queryMonitorStatus(uptimeEsClient, 3, { from: 140000, to: 'now' }, ['id1', 'id2']) + await queryMonitorStatus( + uptimeEsClient, + ['Europe - Germany', 'Asia/Pacific - Japan'], + { from: 140000, to: 'now' }, + ['id1', 'id2'] + ) ).toEqual({ down: 1, enabledIds: ['id1', 'id2'], @@ -311,7 +317,12 @@ describe('current status route', () => { * The expectation here is we will send the test client two separate "requests", one for each of the two IDs. */ expect( - await queryMonitorStatus(uptimeEsClient, 10000, { from: 2500, to: 'now' }, ['id1', 'id2']) + await queryMonitorStatus( + uptimeEsClient, + times(10000).map((n) => 'Europe - Germany' + n), + { from: 2500, to: 'now' }, + ['id1', 'id2'] + ) ).toEqual({ down: 1, enabledIds: ['id1', 'id2'], @@ -344,14 +355,14 @@ describe('current status route', () => { }); expect(esClient.search).toHaveBeenCalledTimes(2); // These assertions are to ensure that we are paginating through the IDs we use for filtering - // @ts-expect-error mock search is not lining up with expected type - expect(esClient.search.mock.calls[0][0].query.bool.filter[2].terms['monitor.id']).toEqual([ - 'id1', - ]); - // @ts-expect-error mock search is not lining up with expected type - expect(esClient.search.mock.calls[1][0].query.bool.filter[2].terms['monitor.id']).toEqual([ - 'id2', - ]); + expect( + // @ts-expect-error mock search is not lining up with expected type + esClient.search.mock.calls[0][0].body.query.bool.filter[2].terms['monitor.id'] + ).toEqual(['id1']); + expect( + // @ts-expect-error mock search is not lining up with expected type + esClient.search.mock.calls[1][0].body.query.bool.filter[2].terms['monitor.id'] + ).toEqual(['id2']); }); }); }); diff --git a/x-pack/plugins/synthetics/server/routes/status/current_status.ts b/x-pack/plugins/synthetics/server/routes/status/current_status.ts index 6fb837ac60ec..159b464312f6 100644 --- a/x-pack/plugins/synthetics/server/routes/status/current_status.ts +++ b/x-pack/plugins/synthetics/server/routes/status/current_status.ts @@ -6,15 +6,18 @@ */ import datemath, { Unit } from '@kbn/datemath'; -import { IKibanaResponse, SavedObjectsClientContract } from '@kbn/core/server'; +import { SavedObjectsClientContract } from '@kbn/core/server'; +import pMap from 'p-map'; +import { getAllMonitors } from '../../saved_objects/synthetics_monitor/get_all_monitors'; +import { UptimeServerSetup } from '../../legacy_uptime/lib/adapters'; +import { getAllLocations } from '../../synthetics_service/get_all_locations'; import { queryMonitorStatus } from '../../queries/query_monitor_status'; import { SYNTHETICS_API_URLS } from '../../../common/constants'; import { UMServerLibs } from '../../legacy_uptime/uptime_server'; import { SyntheticsRestApiRouteFactory } from '../../legacy_uptime/routes'; -import { getMonitors } from '../common'; import { UptimeEsClient } from '../../legacy_uptime/lib/lib'; import { SyntheticsMonitorClient } from '../../synthetics_service/synthetics_monitor/synthetics_monitor_client'; -import { ConfigKey, OverviewStatus } from '../../../common/runtime_types'; +import { ConfigKey, ServiceLocation } from '../../../common/runtime_types'; import { QuerySchema, MonitorsQuery } from '../common'; /** @@ -36,60 +39,91 @@ export function periodToMs(schedule: { number: string; unit: Unit }) { * @returns The counts of up/down/disabled monitor by location, and a map of each monitor:location status. */ export async function getStatus( + server: UptimeServerSetup, uptimeEsClient: UptimeEsClient, - savedObjectsClient: SavedObjectsClientContract, + soClient: SavedObjectsClientContract, syntheticsMonitorClient: SyntheticsMonitorClient, params: MonitorsQuery ) { const { query } = params; - let monitors; const enabledIds: string[] = []; let disabledCount = 0; - let page = 1; let maxPeriod = 0; - let maxLocations = 1; + let listOfLocationsSet = new Set(); + const monitorLocationMap: Record = {}; /** * Walk through all monitor saved objects, bucket IDs by disabled/enabled status. * * Track max period to make sure the snapshot query should reach back far enough to catch * latest ping for all enabled monitors. */ - do { - monitors = await getMonitors( - { - perPage: 500, - page, - sortField: 'name.keyword', - sortOrder: 'asc', - query, - fields: [ - ConfigKey.ENABLED, - ConfigKey.LOCATIONS, - ConfigKey.MONITOR_QUERY_ID, - ConfigKey.SCHEDULE, - ], - }, - syntheticsMonitorClient.syntheticsService, - savedObjectsClient - ); - page++; - monitors.saved_objects.forEach((monitor) => { - const attrs = monitor.attributes; - if (attrs[ConfigKey.ENABLED] === false) { - disabledCount += attrs[ConfigKey.LOCATIONS].length; - } else { - enabledIds.push(attrs[ConfigKey.MONITOR_QUERY_ID]); - maxLocations = Math.max(maxLocations, attrs[ConfigKey.LOCATIONS].length); - maxPeriod = Math.max(maxPeriod, periodToMs(attrs[ConfigKey.SCHEDULE])); - } - }); - } while (monitors.saved_objects.length === monitors.per_page); + + const allMonitors = await getAllMonitors({ + soClient, + search: `${query}*`, + fields: [ + ConfigKey.ENABLED, + ConfigKey.LOCATIONS, + ConfigKey.MONITOR_QUERY_ID, + ConfigKey.SCHEDULE, + ], + }); + + let allLocations: ServiceLocation[] | null = null; + + const getLocationLabel = async (locationId: string) => { + if (!allLocations) { + const { publicLocations, privateLocations } = await getAllLocations( + server, + syntheticsMonitorClient, + soClient + ); + + allLocations = [...publicLocations, ...privateLocations]; + } + + return allLocations.find((loc) => loc.id === locationId)?.label ?? locationId; + }; + + for (const monitor of allMonitors) { + const attrs = monitor.attributes; + if (attrs[ConfigKey.ENABLED] === false) { + disabledCount += attrs[ConfigKey.LOCATIONS].length; + } else { + const missingLabels = new Set(); + + enabledIds.push(attrs[ConfigKey.MONITOR_QUERY_ID]); + const monLocs = new Set([ + ...(attrs[ConfigKey.LOCATIONS] + .filter((loc) => { + if (!loc.label) { + missingLabels.add(loc.id); + } + return loc.label; + }) + .map((location) => location.label) as string[]), + ]); + + // since label wasn't always part of location, there can be a case where we have a location + // with an id but no label. We need to fetch the label from the API + // Adding a migration to add the label to the saved object is a future consideration + const locLabels = await pMap([...missingLabels], async (locationId) => + getLocationLabel(locationId) + ); + + monitorLocationMap[attrs[ConfigKey.MONITOR_QUERY_ID]] = [...monLocs, ...locLabels]; + listOfLocationsSet = new Set([...listOfLocationsSet, ...monLocs, ...locLabels]); + + maxPeriod = Math.max(maxPeriod, periodToMs(attrs[ConfigKey.SCHEDULE])); + } + } const { up, down, upConfigs, downConfigs } = await queryMonitorStatus( uptimeEsClient, - maxLocations, + [...listOfLocationsSet], { from: maxPeriod, to: 'now' }, - enabledIds + enabledIds, + monitorLocationMap ); return { @@ -109,15 +143,49 @@ export const createGetCurrentStatusRoute: SyntheticsRestApiRouteFactory = (libs: query: QuerySchema, }, handler: async ({ + server, uptimeEsClient, savedObjectsClient, syntheticsMonitorClient, - response, request, - }): Promise> => { + }): Promise => { const params = request.query; - return response.ok({ - body: await getStatus(uptimeEsClient, savedObjectsClient, syntheticsMonitorClient, params), - }); + return await getStatus( + server, + uptimeEsClient, + savedObjectsClient, + syntheticsMonitorClient, + params + ); }, }); + +export const resolveMissingLabels = async ( + server: UptimeServerSetup, + savedObjectsClient: SavedObjectsClientContract, + syntheticsMonitorClient: SyntheticsMonitorClient, + listOfLocationsSet: Set, + missingLabelLocations: Set +) => { + const listOfLocations = Array.from(listOfLocationsSet); + + if (missingLabelLocations.size > 0) { + const { publicLocations, privateLocations } = await getAllLocations( + server, + syntheticsMonitorClient, + savedObjectsClient + ); + + const locations = [...publicLocations, ...privateLocations]; + + missingLabelLocations.forEach((id) => { + const location = locations.find((loc) => loc.id === id); + if (location) { + listOfLocations.push(location.label); + } + }); + return { listOfLocations }; + } + + return { listOfLocations }; +}; diff --git a/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts b/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts index 981901eb88e3..c756bcad3063 100644 --- a/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts +++ b/x-pack/plugins/synthetics/server/saved_objects/synthetics_monitor/get_all_monitors.ts @@ -7,16 +7,29 @@ import { SavedObjectsClientContract, + SavedObjectsFindOptions, SavedObjectsFindResult, } from '@kbn/core-saved-objects-api-server'; import { syntheticsMonitorType } from '../../legacy_uptime/lib/saved_objects/synthetics_monitor'; import { EncryptedSyntheticsMonitor } from '../../../common/runtime_types'; -export const getAllMonitors = async (soClient: SavedObjectsClientContract, filters: string) => { +export const getAllMonitors = async ({ + soClient, + search, + fields, + sortField, + sortOrder, +}: { + soClient: SavedObjectsClientContract; + search?: string; +} & Pick) => { const finder = soClient.createPointInTimeFinder({ type: syntheticsMonitorType, perPage: 1000, - search: filters, + search, + sortField, + sortOrder, + fields, }); const hits: Array> = []; diff --git a/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts b/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts index 108899271e1c..3e3eb72700ed 100644 --- a/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts +++ b/x-pack/plugins/synthetics/server/synthetics_route_wrapper.ts @@ -5,7 +5,7 @@ * 2.0. */ import { KibanaResponse } from '@kbn/core-http-router-server-internal'; -import { createUptimeESClient, isTestUser } from './legacy_uptime/lib/lib'; +import { isTestUser, UptimeEsClient } from './legacy_uptime/lib/lib'; import { syntheticsServiceApiKey } from './legacy_uptime/lib/saved_objects/service_api_key'; import { SyntheticsRouteWrapper, SyntheticsStreamingRouteHandler } from './legacy_uptime/routes'; @@ -29,12 +29,13 @@ export const syntheticsRouteWrapper: SyntheticsRouteWrapper = ( // specifically needed for the synthetics service api key generation server.authSavedObjectsClient = savedObjectsClient; - const uptimeEsClient = createUptimeESClient({ - request, + const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - esClient: esClient.asCurrentUser, - uiSettings: coreContext.uiSettings, - }); + esClient.asCurrentUser, + false, + coreContext.uiSettings, + request + ); server.uptimeEsClient = uptimeEsClient; @@ -61,13 +62,13 @@ export const syntheticsRouteWrapper: SyntheticsRouteWrapper = ( // specifically needed for the synthetics service api key generation server.authSavedObjectsClient = savedObjectsClient; - const uptimeEsClient = createUptimeESClient({ - isDev: Boolean(server.isDev) && !isTestUser(server), - uiSettings, - request, + const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - esClient: esClient.asCurrentUser, - }); + esClient.asCurrentUser, + Boolean(server.isDev) && !isTestUser(server), + uiSettings, + request + ); server.uptimeEsClient = uptimeEsClient; From b08afb1d6fb0345f5a4f27acd54915c971877356 Mon Sep 17 00:00:00 2001 From: Ersin Erdal <92688503+ersin-erdal@users.noreply.github.com> Date: Tue, 3 Jan 2023 17:09:43 +0100 Subject: [PATCH 11/30] Rule create/update form re-render (#147221) ON week project This PR reduces the number of re-renders of rule add/edit form by passing the `rule.actions` instead of `rule` to the useEffect hook. Rule add/edit forms take rule object as prop and as every change on any input in the form modifies the rule object, giving it to a hook as a dependency would cause multiple re-renders. --- .../public/application/lib/capabilities.ts | 7 ++-- .../components/rule_definition.tsx | 2 +- .../rule_details/components/rule_details.tsx | 2 +- .../sections/rule_form/rule_add.tsx | 11 +++--- .../sections/rule_form/rule_edit.tsx | 4 +-- .../sections/rule_form/rule_errors.test.tsx | 35 +++++++++---------- .../sections/rule_form/rule_errors.ts | 4 +-- .../sections/rule_form/rule_form.tsx | 4 +-- .../rules_list/components/rules_list.tsx | 2 +- .../components/rules_list_table.tsx | 2 +- 10 files changed, 37 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts index bc4957b65b1b..a995d697e8c5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/capabilities.ts @@ -23,8 +23,11 @@ export const hasExecuteActionsCapability = (capabilities: Capabilities) => export const hasDeleteActionsCapability = (capabilities: Capabilities) => capabilities?.actions?.delete; -export function hasAllPrivilege(rule: InitialRule, ruleType?: RuleType): boolean { - return ruleType?.authorizedConsumers[rule.consumer]?.all ?? false; +export function hasAllPrivilege( + ruleConsumer: InitialRule['consumer'], + ruleType?: RuleType +): boolean { + return ruleType?.authorizedConsumers[ruleConsumer]?.all ?? false; } export function hasReadPrivilege(rule: InitialRule, ruleType?: RuleType): boolean { return ruleType?.authorizedConsumers[rule.consumer]?.read ?? false; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_definition.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_definition.tsx index 6e24030cd60e..0a54b40299ff 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_definition.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_definition.tsx @@ -68,7 +68,7 @@ export const RuleDefinition: React.FunctionComponent = ({ const canExecuteActions = hasExecuteActionsCapability(capabilities); const canSaveRule = rule && - hasAllPrivilege(rule, ruleType) && + hasAllPrivilege(rule.consumer, ruleType) && // if the rule has actions, can the user save the rule's action params (canExecuteActions || (!canExecuteActions && rule.actions.length === 0)); const hasEditButton = useMemo(() => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx index 32a0e35cc2e1..0e06036b2b32 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_details.tsx @@ -156,7 +156,7 @@ export const RuleDetails: React.FunctionComponent = ({ const canExecuteActions = hasExecuteActionsCapability(capabilities); const canSaveRule = - hasAllPrivilege(rule, ruleType) && + hasAllPrivilege(rule.consumer, ruleType) && // if the rule has actions, can the user save the rule's action params (canExecuteActions || (!canExecuteActions && rule.actions.length === 0)); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx index b12a3c07c787..072add4f1f56 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_add.tsx @@ -143,11 +143,11 @@ const RuleAdd = ({ useEffect(() => { (async () => { setIsLoading(true); - const res = await getRuleActionErrors(rule as Rule, actionTypeRegistry); + const res = await getRuleActionErrors(rule.actions, actionTypeRegistry); setIsLoading(false); setRuleActionsErrors([...res]); })(); - }, [rule, actionTypeRegistry]); + }, [rule.actions, actionTypeRegistry]); useEffect(() => { if (config.minimumScheduleInterval && !initialValues?.schedule?.interval) { @@ -196,10 +196,9 @@ const RuleAdd = ({ const ruleType = rule.ruleTypeId ? ruleTypeRegistry.get(rule.ruleTypeId) : null; - const { ruleBaseErrors, ruleErrors, ruleParamsErrors } = getRuleErrors( - rule as Rule, - ruleType, - config + const { ruleBaseErrors, ruleErrors, ruleParamsErrors } = useMemo( + () => getRuleErrors(rule as Rule, ruleType, config), + [rule, ruleType, config] ); // Confirm before saving if user is able to add actions but hasn't added any to this rule diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx index feec862ab804..0f5f4af1ea58 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx @@ -94,11 +94,11 @@ export const RuleEdit = ({ useEffect(() => { (async () => { setIsLoading(true); - const res = await getRuleActionErrors(rule as Rule, actionTypeRegistry); + const res = await getRuleActionErrors(rule.actions, actionTypeRegistry); setRuleActionsErrors([...res]); setIsLoading(false); })(); - }, [rule, actionTypeRegistry]); + }, [rule.actions, actionTypeRegistry]); useEffect(() => { if (!props.ruleType && !serverRuleType) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx index 3453728862b5..9747e42dcf3c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.test.tsx @@ -151,26 +151,25 @@ describe('rule_errors', () => { })), })); const result = await getRuleActionErrors( - mockRule({ - actions: [ - { - id: '1234', - actionTypeId: 'myActionType', - group: '', - params: { - name: 'yes', - }, + [ + { + id: '1234', + actionTypeId: 'myActionType', + group: '', + params: { + name: 'yes', }, - { - id: '5678', - actionTypeId: 'myActionType2', - group: '', - params: { - name: 'yes', - }, + }, + { + id: '5678', + actionTypeId: 'myActionType2', + group: '', + params: { + name: 'yes', }, - ], - }), + }, + ], + actionTypeRegistry ); expect(result).toStrictEqual([ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts index 73769d8d9af1..b57d6b8ab9a4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_errors.ts @@ -101,11 +101,11 @@ export function getRuleErrors( } export async function getRuleActionErrors( - rule: Rule, + actions: RuleAction[], actionTypeRegistry: ActionTypeRegistryContract ): Promise { return await Promise.all( - rule.actions.map( + actions.map( async (ruleAction: RuleAction) => ( await actionTypeRegistry.get(ruleAction.actionTypeId)?.validateParams(ruleAction.params) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx index 16dc1642c584..c06c5179acf6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_form.tsx @@ -197,7 +197,7 @@ export const RuleForm = ({ }, [] ) - .filter((item) => item.ruleType && hasAllPrivilege(rule, item.ruleType)) + .filter((item) => item.ruleType && hasAllPrivilege(rule.consumer, item.ruleType)) .filter((item) => rule.consumer === ALERTS_FEATURE_ID ? !item.ruleTypeModel.requiresAppContext @@ -225,7 +225,7 @@ export const RuleForm = ({ setSolutions( new Map([...solutionsResult.entries()].sort(([, a], [, b]) => a.localeCompare(b))) ); - }, [ruleTypes, ruleTypeIndex, rule.ruleTypeId, kibanaFeatures, rule, ruleTypeRegistry]); + }, [ruleTypes, ruleTypeIndex, rule.ruleTypeId, kibanaFeatures, rule.consumer, ruleTypeRegistry]); useEffect(() => { if (loadRuleTypesError) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx index 76e8f8cbc813..3c5c8013e734 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.tsx @@ -707,7 +707,7 @@ export const RulesList = ({ return selectedIds.length ? filterRulesById(rulesState.data, selectedIds).every((selectedRule) => - hasAllPrivilege(selectedRule, ruleTypesState.data.get(selectedRule.ruleTypeId)) + hasAllPrivilege(selectedRule.consumer, ruleTypesState.data.get(selectedRule.ruleTypeId)) ) : false; }, [selectedIds, rulesState.data, ruleTypesState.data, isAllSelected]); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_table.tsx index 7803393e46a6..f49257ca12c9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list_table.tsx @@ -165,7 +165,7 @@ export function convertRulesToTableItems(opts: ConvertRulesToTableItemsOpts): Ru actionsCount: rule.actions.length, ruleType: ruleTypeIndex.get(rule.ruleTypeId)?.name ?? rule.ruleTypeId, isEditable: - hasAllPrivilege(rule, ruleTypeIndex.get(rule.ruleTypeId)) && + hasAllPrivilege(rule.consumer, ruleTypeIndex.get(rule.ruleTypeId)) && (canExecuteActions || (!canExecuteActions && !rule.actions.length)), enabledInLicense: !!ruleTypeIndex.get(rule.ruleTypeId)?.enabledInLicense, showIntervalWarning: parseDuration(rule.schedule.interval) < minimumDuration, From 0c516dc3c01cbe9fc91bef6f9e2409652645002a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 3 Jan 2023 11:14:35 -0500 Subject: [PATCH 12/30] Add wait for before asserting user event (#147941) Resolves https://github.com/elastic/kibana/issues/147396. In this PR, I'm fixing the flaky test where a function sometimes appears to not have been called. My fix uses `waitFor` for the `onConfirm` to be called before asserting the call arguments. Since the flaky test runner doesn't work with jest tests, I had to put the flaky test in a for loop to confirm it doesn't cause issues anymore: - Commit looping 100 times to ensure no more flakiness: https://github.com/elastic/kibana/pull/147941/commits/d39ca89e7ef033f4ce846f679f66401c7504b8a2?w=1 ([CI Run](https://buildkite.com/elastic/kibana-pull-request/builds/96590)) - Another commit looping 150 times to double-check no more flakiness: https://github.com/elastic/kibana/pull/147941/commits/9aeec4f120c571cdc4d205275e46245219809cb2?w=1 ([CI Run](https://buildkite.com/elastic/kibana-pull-request/builds/96626)) --- .../lib/servicenow/update_connector.test.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.test.tsx b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.test.tsx index baf8a4d9a996..2f7aa35b56c3 100644 --- a/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.test.tsx +++ b/x-pack/plugins/stack_connectors/public/connector_types/lib/servicenow/update_connector.test.tsx @@ -11,7 +11,7 @@ import userEvent from '@testing-library/user-event'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { Props, UpdateConnector } from './update_connector'; import { act } from 'react-dom/test-utils'; -import { render, act as reactAct } from '@testing-library/react'; +import { render, act as reactAct, waitFor } from '@testing-library/react'; jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana'); @@ -30,8 +30,7 @@ const mountUpdateConnector = (props: Partial = {}, isOAuth: boolean = fal ); }; -// FLAKY: https://github.com/elastic/kibana/issues/147396 -describe.skip('UpdateConnector renders', () => { +describe('UpdateConnector renders', () => { it('should render update connector fields', () => { const wrapper = mountUpdateConnector(); @@ -247,6 +246,9 @@ describe.skip('UpdateConnector renders', () => { userEvent.click(getByTestId('snUpdateInstallationSubmit')); }); + // Wait for click event to be processed + await waitFor(() => expect(onConfirm).toHaveBeenCalled(), { timeout: 3000 }); + expect(onConfirm).toHaveBeenCalledWith({ config: { apiUrl: 'https://example.com', From 226dabfc10f14052751fb93dacb45b1b5cc6ba14 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Tue, 3 Jan 2023 10:35:50 -0600 Subject: [PATCH 13/30] [DOCS] Update create-panels-with-editors.asciidoc Bucket Script supported with TSVB (#148315) ## Summary Opens #147692 in `main`. --- docs/user/dashboard/create-panels-with-editors.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/dashboard/create-panels-with-editors.asciidoc b/docs/user/dashboard/create-panels-with-editors.asciidoc index 4b60b808a15a..ff61e03b381f 100644 --- a/docs/user/dashboard/create-panels-with-editors.asciidoc +++ b/docs/user/dashboard/create-panels-with-editors.asciidoc @@ -469,7 +469,7 @@ Pipeline aggregations are dependent on the outputs calculated from other aggrega | Bucket script | -| +| ✓ | ✓ | ✓ From 2dbbd8a30497ef3982eb03ff266fc62732f21ca5 Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Tue, 3 Jan 2023 16:36:08 +0000 Subject: [PATCH 14/30] [APM] Errors group sampler (#147571) Closes https://github.com/elastic/kibana/issues/146975 https://user-images.githubusercontent.com/31922082/209793125-0e8a5d5e-c867-4734-a800-cecbaa142b0a.mov --- .../detail_view/index.test.tsx | 168 ------------- .../error_sample_detail.tsx} | 235 +++++++++++++----- .../error_tabs.tsx | 17 +- .../exception_stacktrace.stories.tsx | 0 .../exception_stacktrace.test.tsx | 0 .../exception_stacktrace.tsx | 0 .../error_sampler/index.tsx | 99 ++++++++ .../error_sampler/sample_summary.tsx | 79 ++++++ .../app/error_group_details/index.tsx | 228 ++++++++--------- .../routing/service_detail/index.tsx | 1 + .../components/shared/summary/index.tsx | 29 +-- .../errors/__snapshots__/queries.test.ts.snap | 192 -------------- ...ample.ts => get_error_group_sample_ids.ts} | 36 ++- .../get_error_sample_details.ts | 73 ++++++ .../apm/server/routes/errors/queries.test.ts | 72 ------ .../plugins/apm/server/routes/errors/route.ts | 52 +++- .../apm/typings/es_schemas/raw/error_raw.ts | 2 +- .../translations/translations/fr-FR.json | 7 - .../translations/translations/ja-JP.json | 7 - .../translations/translations/zh-CN.json | 7 - ...up_id.spec.ts => group_id_samples.spec.ts} | 76 +++++- .../tests/feature_controls.spec.ts | 2 +- 22 files changed, 660 insertions(+), 722 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.test.tsx rename x-pack/plugins/apm/public/components/app/error_group_details/{detail_view/index.tsx => error_sampler/error_sample_detail.tsx} (50%) rename x-pack/plugins/apm/public/components/app/error_group_details/{detail_view => error_sampler}/error_tabs.tsx (68%) rename x-pack/plugins/apm/public/components/app/error_group_details/{detail_view => error_sampler}/exception_stacktrace.stories.tsx (100%) rename x-pack/plugins/apm/public/components/app/error_group_details/{detail_view => error_sampler}/exception_stacktrace.test.tsx (100%) rename x-pack/plugins/apm/public/components/app/error_group_details/{detail_view => error_sampler}/exception_stacktrace.tsx (100%) create mode 100644 x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/index.tsx create mode 100644 x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/sample_summary.tsx delete mode 100644 x-pack/plugins/apm/server/routes/errors/__snapshots__/queries.test.ts.snap rename x-pack/plugins/apm/server/routes/errors/get_error_groups/{get_error_group_sample.ts => get_error_group_sample_ids.ts} (73%) create mode 100644 x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts delete mode 100644 x-pack/plugins/apm/server/routes/errors/queries.test.ts rename x-pack/test/apm_api_integration/tests/errors/{group_id.spec.ts => group_id_samples.spec.ts} (52%) diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.test.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.test.tsx deleted file mode 100644 index ac38a84bf47d..000000000000 --- a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.test.tsx +++ /dev/null @@ -1,168 +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 { render } from '@testing-library/react'; -import React from 'react'; -import { mockMoment } from '../../../../utils/test_helpers'; -import { DetailView } from '.'; -import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; -import { createMemoryHistory } from 'history'; -import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; - -const history = createMemoryHistory({ - initialEntries: [ - '/services/opbeans-java/errors/0000?rangeFrom=now-15m&rangeTo=now', - ], -}); - -function MockContext({ children }: { children: React.ReactElement }) { - return ( - - - {children} - - - ); -} - -function renderWithMockContext(element: React.ReactElement) { - return render(element, { wrapper: MockContext }); -} - -describe('DetailView', () => { - beforeEach(() => { - // Avoid timezone issues - mockMoment(); - }); - - it('should render empty state', () => { - const wrapper = renderWithMockContext( - - ); - expect(wrapper.baseElement.innerHTML).toBe('
'); - }); - - it('should render Discover button', () => { - const errorGroup = { - occurrencesCount: 10, - transaction: undefined, - error: { - timestamp: { - us: 0, - }, - http: { request: { method: 'GET' } }, - url: { full: 'myUrl' }, - service: { name: 'myService' }, - user: { id: 'myUserId' }, - error: { exception: [{ handled: true }] }, - transaction: { id: 'myTransactionId', sampled: true }, - } as any, - }; - - const discoverLink = renderWithMockContext( - - ).getByText(`View 10 occurrences in Discover`); - - expect(discoverLink).toBeInTheDocument(); - }); - - it('should render a Summary', () => { - const errorGroup = { - occurrencesCount: 10, - error: { - service: { - name: 'opbeans-python', - }, - error: {}, - timestamp: { - us: 0, - }, - } as any, - transaction: undefined, - }; - - const rendered = renderWithMockContext( - - ); - - expect( - rendered.getByText('1337 minutes ago (mocking 0)') - ).toBeInTheDocument(); - }); - - it('should render tabs', () => { - const errorGroup = { - occurrencesCount: 10, - transaction: undefined, - error: { - timestamp: { - us: 0, - }, - error: {}, - service: {}, - user: {}, - } as any, - }; - - const rendered = renderWithMockContext( - - ); - - expect(rendered.getByText('Exception stack trace')).toBeInTheDocument(); - - expect(rendered.getByText('Metadata')).toBeInTheDocument(); - }); - - it('should render TabContent', () => { - const errorGroup = { - occurrencesCount: 10, - transaction: undefined, - error: { - service: { - name: 'opbeans-python', - }, - timestamp: { - us: 0, - }, - error: { - exception: [{ handled: true }], - }, - context: {}, - } as any, - }; - const rendered = renderWithMockContext( - - ); - - expect(rendered.getByText('No stack trace available.')).toBeInTheDocument(); - }); - - it('should render without http request info', () => { - const errorGroup = { - occurrencesCount: 10, - transaction: undefined, - error: { - timestamp: { - us: 0, - }, - error: { - exception: [{ handled: true }], - }, - http: { response: { status_code: 404 } }, - url: { full: 'myUrl' }, - service: { name: 'myService' }, - user: { id: 'myUserId' }, - transaction: { id: 'myTransactionId', sampled: true }, - } as any, - }; - expect(() => - renderWithMockContext( - - ) - ).not.toThrowError(); - }); -}); diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx similarity index 50% rename from x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.tsx rename to x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx index 22afb5198a1c..397bfa5e4c1a 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_sample_detail.tsx @@ -16,15 +16,16 @@ import { EuiTabs, EuiTitle, EuiToolTip, + EuiEmptyPrompt, + EuiPagination, + EuiLoadingContent, + EuiBadge, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { first } from 'lodash'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import type { APIReturnType } from '../../../../services/rest/create_call_apm_api'; -import type { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; -import type { ApmUrlParams } from '../../../../context/url_params_context/types'; import { TransactionDetailLink } from '../../../shared/links/apm/transaction_detail_link'; import { DiscoverErrorLink } from '../../../shared/links/discover_links/discover_error_link'; import { fromQuery, toQuery } from '../../../shared/links/url_helpers'; @@ -47,6 +48,11 @@ import { ERROR_GROUP_ID } from '../../../../../common/es_fields/apm'; import { TraceSearchType } from '../../../../../common/trace_explorer'; import { TransactionTab } from '../../transaction_details/waterfall_with_summary/transaction_tabs'; import { useTraceExplorerEnabledSetting } from '../../../../hooks/use_trace_explorer_enabled_setting'; +import { FETCH_STATUS, isPending } from '../../../../hooks/use_fetcher'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; +import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; +import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { SampleSummary } from './sample_summary'; const TransactionLinkName = euiStyled.div` margin-left: ${({ theme }) => theme.eui.euiSizeS}; @@ -55,25 +61,35 @@ const TransactionLinkName = euiStyled.div` `; interface Props { - errorGroup: APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}'>; - urlParams: ApmUrlParams; - kuery: string; + onSampleClick: (sample: string) => void; + errorSampleIds: string[]; + errorSamplesFetchStatus: FETCH_STATUS; + errorData: APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}'>; + errorFetchStatus: FETCH_STATUS; + occurrencesCount: number; } -// TODO: Move query-string-based tabs into a re-usable component? function getCurrentTab( tabs: ErrorTab[] = [], currentTabKey: string | undefined ): ErrorTab | {} { const selectedTab = tabs.find(({ key }) => key === currentTabKey); - return selectedTab ? selectedTab : first(tabs) || {}; + return selectedTab ?? (first(tabs) || {}); } -export function DetailView({ errorGroup, urlParams, kuery }: Props) { +export function ErrorSampleDetails({ + onSampleClick, + errorSampleIds, + errorSamplesFetchStatus, + errorData, + errorFetchStatus, + occurrencesCount, +}: Props) { + const [sampleActivePage, setSampleActivePage] = useState(0); const history = useHistory(); - const { transaction, error, occurrencesCount } = errorGroup; - - const { detailTab, offset, comparisonEnabled } = urlParams; + const { + urlParams: { detailTab, offset, comparisonEnabled }, + } = useLegacyUrlParams(); const router = useApmRouter(); @@ -84,17 +100,51 @@ export function DetailView({ errorGroup, urlParams, kuery }: Props) { query, } = useApmParams('/services/{serviceName}/errors/{groupId}'); - if (!error) { - return null; + const { kuery } = query; + + const loadingErrorSamplesData = isPending(errorSamplesFetchStatus); + const loadingErrorData = isPending(errorFetchStatus); + const isLoading = loadingErrorSamplesData || loadingErrorData; + + const isSucceded = + errorSamplesFetchStatus === FETCH_STATUS.SUCCESS && + errorFetchStatus === FETCH_STATUS.SUCCESS; + + useEffect(() => { + setSampleActivePage(0); + }, [errorSampleIds]); + + const goToSample = (index: number) => { + const sample = errorSampleIds[index]; + setSampleActivePage(index); + onSampleClick(sample); + }; + + const { error, transaction } = errorData; + + if (!error && errorSampleIds?.length === 0 && isSucceded) { + return ( + + {i18n.translate('xpack.apm.errorSampleDetails.sampleNotFound', { + defaultMessage: 'The selected error cannot be found', + })} +
+ } + titleSize="s" + /> + ); } const tabs = getTabs(error); const currentTab = getCurrentTab(tabs, detailTab) as ErrorTab; const errorUrl = error.error.page?.url || error.url?.full; - const method = error.http?.request?.method; const status = error.http?.response?.status_code; + const environment = error.service.environment; + const serviceVersion = error.service.version; const traceExplorerLink = router.link('/traces/explorer/waterfall', { query: { @@ -112,18 +162,28 @@ export function DetailView({ errorGroup, urlParams, kuery }: Props) { return ( - +

{i18n.translate( - 'xpack.apm.errorGroupDetails.errorOccurrenceTitle', + 'xpack.apm.errorSampleDetails.errorOccurrenceTitle', { - defaultMessage: 'Error occurrence', + defaultMessage: 'Error sample', } )}

+ + {!!errorSampleIds?.length && ( + + )} + {isTraceExplorerEnabled && ( @@ -133,7 +193,7 @@ export function DetailView({ errorGroup, urlParams, kuery }: Props) { {i18n.translate( - 'xpack.apm.errorGroupDetails.viewOccurrencesInTraceExplorer', + 'xpack.apm.errorSampleDetails.viewOccurrencesInTraceExplorer', { defaultMessage: 'Explore traces with this error', } @@ -151,7 +211,7 @@ export function DetailView({ errorGroup, urlParams, kuery }: Props) { {i18n.translate( - 'xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel', + 'xpack.apm.errorSampleDetails.viewOccurrencesInDiscoverButtonLabel', { defaultMessage: 'View {occurrencesCount} {occurrencesCount, plural, one {occurrence} other {occurrences}} in Discover', @@ -163,49 +223,90 @@ export function DetailView({ errorGroup, urlParams, kuery }: Props) {
- -
, - errorUrl && method ? ( - - ) : null, - transaction && transaction.user_agent ? ( - - ) : null, - transaction && ( - - + {isLoading ? ( + + + + + ) : ( + , + errorUrl && method ? ( + + ) : null, + transaction && transaction.user_agent ? ( + + ) : null, + transaction && ( + - - - {transaction.transaction.name} - - - - ), - ]} - /> + + + + {transaction.transaction.name} + + + + ), + environment ? ( + + {environment} + + ) : null, + serviceVersion ? ( + + {serviceVersion} + + ) : null, + ]} + /> + )} + {isLoading ? ( + + + + + ) : ( + + )} {tabs.map(({ key, label }) => { @@ -229,7 +330,11 @@ export function DetailView({ errorGroup, urlParams, kuery }: Props) { })} - + {isLoading || !error ? ( + + ) : ( + + )} ); } @@ -241,9 +346,9 @@ function TabContent({ error: APMError; currentTab: ErrorTab; }) { - const codeLanguage = error.service.language?.name; - const exceptions = error.error.exception || []; - const logStackframes = error.error.log?.stacktrace; + const codeLanguage = error?.service.language?.name; + const exceptions = error?.error.exception || []; + const logStackframes = error?.error.log?.stacktrace; switch (currentTab.key) { case logStacktraceTab.key: diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/error_tabs.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_tabs.tsx similarity index 68% rename from x-pack/plugins/apm/public/components/app/error_group_details/detail_view/error_tabs.tsx rename to x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_tabs.tsx index e5f0ba4e834a..d6d487b730ca 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/error_tabs.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/error_tabs.tsx @@ -10,36 +10,33 @@ import { isEmpty } from 'lodash'; import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; export interface ErrorTab { - key: 'log_stacktrace' | 'exception_stacktrace' | 'metadata'; + key: 'log_stacktrace' | 'exception_stacktrace' | 'metadata' | 'summary'; label: string; } export const logStacktraceTab: ErrorTab = { key: 'log_stacktrace', - label: i18n.translate('xpack.apm.propertiesTable.tabs.logStacktraceLabel', { + label: i18n.translate('xpack.apm.errorGroup.tabs.logStacktraceLabel', { defaultMessage: 'Log stack trace', }), }; export const exceptionStacktraceTab: ErrorTab = { key: 'exception_stacktrace', - label: i18n.translate( - 'xpack.apm.propertiesTable.tabs.exceptionStacktraceLabel', - { - defaultMessage: 'Exception stack trace', - } - ), + label: i18n.translate('xpack.apm.errorGroup.tabs.exceptionStacktraceLabel', { + defaultMessage: 'Exception stack trace', + }), }; export const metadataTab: ErrorTab = { key: 'metadata', - label: i18n.translate('xpack.apm.propertiesTable.tabs.metadataLabel', { + label: i18n.translate('xpack.apm.errorGroup.tabs.metadataLabel', { defaultMessage: 'Metadata', }), }; export function getTabs(error: APMError) { - const hasLogStacktrace = !isEmpty(error.error.log?.stacktrace); + const hasLogStacktrace = !isEmpty(error?.error.log?.stacktrace); return [ ...(hasLogStacktrace ? [logStacktraceTab] : []), exceptionStacktraceTab, diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/exception_stacktrace.stories.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/exception_stacktrace.stories.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/error_group_details/detail_view/exception_stacktrace.stories.tsx rename to x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/exception_stacktrace.stories.tsx diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/exception_stacktrace.test.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/exception_stacktrace.test.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/error_group_details/detail_view/exception_stacktrace.test.tsx rename to x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/exception_stacktrace.test.tsx diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/detail_view/exception_stacktrace.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/exception_stacktrace.tsx similarity index 100% rename from x-pack/plugins/apm/public/components/app/error_group_details/detail_view/exception_stacktrace.tsx rename to x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/exception_stacktrace.tsx diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/index.tsx new file mode 100644 index 000000000000..e5b13aa0df21 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/index.tsx @@ -0,0 +1,99 @@ +/* + * 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 { EuiLoadingSpinner } from '@elastic/eui'; +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { fromQuery, toQuery } from '../../../shared/links/url_helpers'; +import { useApmParams } from '../../../../hooks/use_apm_params'; +import { + FETCH_STATUS, + isPending, + useFetcher, +} from '../../../../hooks/use_fetcher'; +import { useTimeRange } from '../../../../hooks/use_time_range'; +import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; +import { ErrorSampleDetails } from './error_sample_detail'; + +interface Props { + errorSampleIds: string[]; + errorSamplesFetchStatus: FETCH_STATUS; + occurrencesCount: number; +} + +export function ErrorSampler({ + errorSampleIds, + errorSamplesFetchStatus, + occurrencesCount, +}: Props) { + const history = useHistory(); + + const { serviceName } = useApmServiceContext(); + + const { + path: { groupId }, + query, + } = useApmParams('/services/{serviceName}/errors/{groupId}'); + + const { rangeFrom, rangeTo, environment, kuery, errorId } = query; + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + + const { data: errorData, status: errorFetchStatus } = useFetcher( + (callApmApi) => { + if (start && end && errorId) { + return callApmApi( + 'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}', + { + params: { + path: { + serviceName, + groupId, + errorId, + }, + query: { + environment, + kuery, + start, + end, + }, + }, + } + ); + } + }, + [environment, kuery, serviceName, start, end, groupId, errorId] + ); + const onSampleClick = (sample: string) => { + history.push({ + ...history.location, + search: fromQuery({ + ...toQuery(history.location.search), + errorId: sample, + }), + }); + }; + const loadingErrorSamplesData = isPending(errorSamplesFetchStatus); + + if (loadingErrorSamplesData || !errorData) { + return ( +
+ +
+ ); + } + + return ( + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/sample_summary.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/sample_summary.tsx new file mode 100644 index 000000000000..af5bd2cc46b1 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/error_group_details/error_sampler/sample_summary.tsx @@ -0,0 +1,79 @@ +/* + * 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 { EuiText, EuiSpacer, EuiCodeBlock, EuiBadge } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; + +const Label = euiStyled.div` + margin-bottom: ${({ theme }) => theme.eui.euiSizeXS}; + font-size: ${({ theme }) => theme.eui.euiFontSizeS}; + color: ${({ theme }) => theme.eui.euiColorDarkestShade}; +`; + +interface Props { + error: APMError; +} +export function SampleSummary({ error }: Props) { + const logMessage = error.error.log?.message; + const excMessage = error.error.exception?.[0].message; + const culprit = error.error.culprit; + const isUnhandled = error.error.exception?.[0].handled === false; + + return ( + <> + {isUnhandled && ( + <> + + {i18n.translate('xpack.apm.errorGroupDetails.unhandledLabel', { + defaultMessage: 'Unhandled', + })} + + + + )} + {logMessage && ( + <> + + + + + {logMessage} + + + )} + + + + + + {excMessage || NOT_AVAILABLE_LABEL} + + + + + + + {culprit || NOT_AVAILABLE_LABEL} + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx index dcde173bffb1..f80d5f741df4 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/index.tsx @@ -11,48 +11,31 @@ import { EuiFlexItem, EuiPanel, EuiSpacer, - EuiText, EuiTitle, - EuiHorizontalRule, + EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import React, { useEffect } from 'react'; +import { omit } from 'lodash'; +import { useHistory } from 'react-router-dom'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; -import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; import { useApmParams } from '../../../hooks/use_apm_params'; import { useApmRouter } from '../../../hooks/use_apm_router'; import { useErrorGroupDistributionFetcher } from '../../../hooks/use_error_group_distribution_fetcher'; -import { useFetcher } from '../../../hooks/use_fetcher'; +import { + FETCH_STATUS, + isPending, + useFetcher, +} from '../../../hooks/use_fetcher'; import { useTimeRange } from '../../../hooks/use_time_range'; import type { APIReturnType } from '../../../services/rest/create_call_apm_api'; -import { DetailView } from './detail_view'; +import { ErrorSampler } from './error_sampler'; import { ErrorDistribution } from './distribution'; import { TopErroneousTransactions } from './top_erroneous_transactions'; - -const Titles = euiStyled.div` - margin-bottom: ${({ theme }) => theme.eui.euiSizeL}; -`; - -const Label = euiStyled.div` - margin-bottom: ${({ theme }) => theme.eui.euiSizeXS}; - font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; - color: ${({ theme }) => theme.eui.euiColorDarkShade}; -`; - -const Message = euiStyled.div` - font-family: ${({ theme }) => theme.eui.euiCodeFontFamily}; - font-weight: bold; - font-size: ${({ theme }) => theme.eui.euiFontSizeM}; - margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; -`; - -const Culprit = euiStyled.div` - font-family: ${({ theme }) => theme.eui.euiCodeFontFamily}; - margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; -`; +import { maybe } from '../../../../common/utils/maybe'; +import { fromQuery, toQuery } from '../../shared/links/url_helpers'; type ErrorDistributionAPIResponse = APIReturnType<'GET /internal/apm/services/{serviceName}/errors/distribution'>; @@ -73,10 +56,10 @@ function getShortGroupId(errorGroupId?: string) { function ErrorGroupHeader({ groupId, - isUnhandled, + occurrencesCount, }: { groupId: string; - isUnhandled?: boolean; + occurrencesCount?: number; }) { return ( @@ -92,26 +75,23 @@ function ErrorGroupHeader({ - - {isUnhandled && ( - - - {i18n.translate('xpack.apm.errorGroupDetails.unhandledLabel', { - defaultMessage: 'Unhandled', - })} - - - )} + + + {i18n.translate('xpack.apm.errorGroupDetails.occurrencesLabel', { + defaultMessage: '{occurrencesCount} occ', + values: { occurrencesCount }, + })} + + ); } export function ErrorGroupDetails() { - const { urlParams } = useLegacyUrlParams(); - const { serviceName } = useApmServiceContext(); const apmRouter = useApmRouter(); + const history = useHistory(); const { path: { groupId }, @@ -122,6 +102,7 @@ export function ErrorGroupDetails() { kuery, serviceGroup, comparisonEnabled, + errorId, }, } = useApmParams('/services/{serviceName}/errors/{groupId}'); @@ -158,105 +139,92 @@ export function ErrorGroupDetails() { ] ); - const { data: errorGroupData } = useFetcher( - (callApmApi) => { - if (start && end) { - return callApmApi( - 'GET /internal/apm/services/{serviceName}/errors/{groupId}', - { - params: { - path: { - serviceName, - groupId, - }, - query: { - environment, - kuery, - start, - end, + const { data: errorSamplesData, status: errorSamplesFetchStatus } = + useFetcher( + (callApmApi) => { + if (start && end) { + return callApmApi( + 'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples', + { + params: { + path: { + serviceName, + groupId, + }, + query: { + environment, + kuery, + start, + end, + }, }, - }, - } - ); - } - }, - [environment, kuery, serviceName, start, end, groupId] - ); - - const { errorDistributionData, status } = useErrorGroupDistributionFetcher({ - serviceName, - groupId, - environment, - kuery, - }); + } + ); + } + }, + [environment, kuery, serviceName, start, end, groupId] + ); + + const { errorDistributionData, status: errorDistributionStatus } = + useErrorGroupDistributionFetcher({ + serviceName, + groupId, + environment, + kuery, + }); + + useEffect(() => { + const selectedSample = errorSamplesData?.errorSampleIds.find( + (sample) => sample === errorId + ); + + if (errorSamplesFetchStatus === FETCH_STATUS.SUCCESS && !selectedSample) { + // selected sample was not found. select a new one: + const selectedErrorId = maybe(errorSamplesData?.errorSampleIds[0]); + + history.replace({ + ...history.location, + search: fromQuery({ + ...omit(toQuery(history.location.search), ['errorId']), + errorId: selectedErrorId, + }), + }); + } + }, [history, errorId, errorSamplesData, errorSamplesFetchStatus]); + + const loadingDistributionData = isPending(errorDistributionStatus); + const loadingErrorSamplesData = isPending(errorSamplesFetchStatus); + + if (loadingDistributionData && loadingErrorSamplesData) { + return ( +
+ +
+ ); + } - if (!errorGroupData || !errorDistributionData) { + if (!errorDistributionData || !errorSamplesData) { return ; } - // If there are 0 occurrences, show only distribution chart w. empty message - const showDetails = errorGroupData.occurrencesCount !== 0; - const logMessage = errorGroupData.error?.error.log?.message; - const excMessage = errorGroupData.error?.error.exception?.[0].message; - const culprit = errorGroupData.error?.error.culprit; - const isUnhandled = - errorGroupData.error?.error.exception?.[0].handled === false; + // If there are 0 occurrences, show only charts w. empty message + const showDetails = errorSamplesData.occurrencesCount !== 0; return ( <> - + - - {showDetails && ( - - - {logMessage && ( - <> - - {logMessage} - - )} - - {excMessage || NOT_AVAILABLE_LABEL} - - {culprit || NOT_AVAILABLE_LABEL} - - - {errorGroupData.occurrencesCount} - - - - )} {showDetails && ( - )} diff --git a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx index d2e84cf8e4a8..9625c35b1a01 100644 --- a/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/service_detail/index.tsx @@ -238,6 +238,7 @@ export const serviceDetail = { path: t.type({ groupId: t.string, }), + query: t.partial({ errorId: t.string }), }), }, '/services/{serviceName}/errors': { diff --git a/x-pack/plugins/apm/public/components/shared/summary/index.tsx b/x-pack/plugins/apm/public/components/shared/summary/index.tsx index e0f6c1e09097..99c468a74042 100644 --- a/x-pack/plugins/apm/public/components/shared/summary/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/summary/index.tsx @@ -5,39 +5,26 @@ * 2.0. */ -import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { Fragment } from 'react'; import { Maybe } from '../../../../typings/common'; interface Props { items: Array>; } -const Item = euiStyled(EuiFlexItem)` - flex-wrap: nowrap; - border-right: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; - padding-right: ${({ theme }) => theme.eui.euiSizeS}; - flex-flow: row nowrap; - line-height: 1.5; - align-items: center !important; - &:last-child { - border-right: none; - padding-right: 0; - } -`; - function Summary({ items }: Props) { const filteredItems = items.filter(Boolean) as React.ReactElement[]; return ( - + {filteredItems.map((item, index) => ( - - {item} - + + {index > 0 && |} + {item} + ))} - + ); } diff --git a/x-pack/plugins/apm/server/routes/errors/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/routes/errors/__snapshots__/queries.test.ts.snap deleted file mode 100644 index ac9ff28ef851..000000000000 --- a/x-pack/plugins/apm/server/routes/errors/__snapshots__/queries.test.ts.snap +++ /dev/null @@ -1,192 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`error queries fetches a single error group 1`] = ` -Object { - "apm": Object { - "events": Array [ - "error", - ], - }, - "body": Object { - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "serviceName", - }, - }, - Object { - "term": Object { - "error.grouping_key": "groupId", - }, - }, - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, - ], - "should": Array [ - Object { - "term": Object { - "transaction.sampled": true, - }, - }, - ], - }, - }, - "size": 1, - "sort": Array [ - Object { - "_score": Object { - "order": "desc", - }, - }, - Object { - "@timestamp": Object { - "order": "desc", - }, - }, - ], - "track_total_hits": true, - }, -} -`; - -exports[`error queries fetches multiple error groups 1`] = ` -Object { - "apm": Object { - "events": Array [ - "error", - ], - }, - "body": Object { - "aggs": Object { - "error_groups": Object { - "aggs": Object { - "sample": Object { - "top_hits": Object { - "_source": Array [ - "error.log.message", - "error.exception.message", - "error.exception.handled", - "error.exception.type", - "error.culprit", - "error.grouping_key", - "@timestamp", - ], - "size": 1, - "sort": Object { - "@timestamp": "desc", - }, - }, - }, - }, - "terms": Object { - "field": "error.grouping_key", - "order": Object { - "_count": "asc", - }, - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "serviceName", - }, - }, - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, - ], - }, - }, - "size": 0, - "track_total_hits": false, - }, -} -`; - -exports[`error queries fetches multiple error groups when sortField = lastSeen 1`] = ` -Object { - "apm": Object { - "events": Array [ - "error", - ], - }, - "body": Object { - "aggs": Object { - "error_groups": Object { - "aggs": Object { - "max_timestamp": Object { - "max": Object { - "field": "@timestamp", - }, - }, - "sample": Object { - "top_hits": Object { - "_source": Array [ - "error.log.message", - "error.exception.message", - "error.exception.handled", - "error.exception.type", - "error.culprit", - "error.grouping_key", - "@timestamp", - ], - "size": 1, - "sort": Object { - "@timestamp": "desc", - }, - }, - }, - }, - "terms": Object { - "field": "error.grouping_key", - "order": Object { - "max_timestamp": "asc", - }, - "size": 500, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "serviceName", - }, - }, - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, - ], - }, - }, - "size": 0, - "track_total_hits": false, - }, -} -`; diff --git a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample.ts b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample_ids.ts similarity index 73% rename from x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample.ts rename to x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample_ids.ts index 20615dc3be53..695210e9e4a8 100644 --- a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample.ts +++ b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_group_sample_ids.ts @@ -10,14 +10,16 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { ERROR_GROUP_ID, + ERROR_ID, SERVICE_NAME, TRANSACTION_SAMPLED, } from '../../../../common/es_fields/apm'; import { environmentQuery } from '../../../../common/utils/environment_query'; -import { getTransaction } from '../../transactions/get_transaction'; import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; -export async function getErrorGroupSample({ +const ERROR_SAMPLES_SIZE = 10000; + +export async function getErrorGroupSampleIds({ environment, kuery, serviceName, @@ -39,8 +41,8 @@ export async function getErrorGroupSample({ events: [ProcessorEvent.error as const], }, body: { - track_total_hits: true, - size: 1, + track_total_hits: ERROR_SAMPLES_SIZE, + size: ERROR_SAMPLES_SIZE, query: { bool: { filter: [ @@ -50,9 +52,10 @@ export async function getErrorGroupSample({ ...environmentQuery(environment), ...kqlQuery(kuery), ], - should: [{ term: { [TRANSACTION_SAMPLED]: true } }], + should: [{ term: { [TRANSACTION_SAMPLED]: true } }], // prefer error samples with related transactions }, }, + _source: [ERROR_ID], sort: asMutableArray([ { _score: { order: 'desc' } }, // sort by _score first to ensure that errors with transaction.sampled:true ends up on top { '@timestamp': { order: 'desc' } }, // sort by timestamp to get the most recent error @@ -60,25 +63,14 @@ export async function getErrorGroupSample({ }, }; - const resp = await apmEventClient.search('get_error_group_sample', params); - const error = resp.hits.hits[0]?._source; - const transactionId = error?.transaction?.id; - const traceId = error?.trace?.id; - - let transaction; - if (transactionId && traceId) { - transaction = await getTransaction({ - transactionId, - traceId, - apmEventClient, - start, - end, - }); - } + const resp = await apmEventClient.search( + 'get_error_group_sample_ids', + params + ); + const errorSampleIds = resp.hits.hits.map((item) => item._source.error.id); return { - transaction, - error, + errorSampleIds, occurrencesCount: resp.hits.total.value, }; } diff --git a/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts new file mode 100644 index 000000000000..de54e24f3946 --- /dev/null +++ b/x-pack/plugins/apm/server/routes/errors/get_error_groups/get_error_sample_details.ts @@ -0,0 +1,73 @@ +/* + * 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 { rangeQuery, kqlQuery } from '@kbn/observability-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { ERROR_ID, SERVICE_NAME } from '../../../../common/es_fields/apm'; +import { environmentQuery } from '../../../../common/utils/environment_query'; +import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import { getTransaction } from '../../transactions/get_transaction'; + +export async function getErrorSampleDetails({ + environment, + kuery, + serviceName, + errorId, + apmEventClient, + start, + end, +}: { + environment: string; + kuery: string; + serviceName: string; + errorId: string; + apmEventClient: APMEventClient; + start: number; + end: number; +}) { + const params = { + apm: { + events: [ProcessorEvent.error as const], + }, + body: { + track_total_hits: false, + size: 1, + query: { + bool: { + filter: [ + { term: { [SERVICE_NAME]: serviceName } }, + { term: { [ERROR_ID]: errorId } }, + ...rangeQuery(start, end), + ...environmentQuery(environment), + ...kqlQuery(kuery), + ], + }, + }, + }, + }; + + const resp = await apmEventClient.search('get_error_sample_details', params); + const error = resp.hits.hits[0]?._source; + const transactionId = error?.transaction?.id; + const traceId = error?.trace?.id; + + let transaction; + if (transactionId && traceId) { + transaction = await getTransaction({ + transactionId, + traceId, + apmEventClient, + start, + end, + }); + } + + return { + transaction, + error, + }; +} diff --git a/x-pack/plugins/apm/server/routes/errors/queries.test.ts b/x-pack/plugins/apm/server/routes/errors/queries.test.ts deleted file mode 100644 index bc0f18884d1b..000000000000 --- a/x-pack/plugins/apm/server/routes/errors/queries.test.ts +++ /dev/null @@ -1,72 +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 { - SearchParamsMock, - inspectSearchParams, -} from '../../utils/test_helpers'; -import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; -import { getErrorGroupMainStatistics } from './get_error_groups/get_error_group_main_statistics'; -import { getErrorGroupSample } from './get_error_groups/get_error_group_sample'; - -describe('error queries', () => { - let mock: SearchParamsMock; - - afterEach(() => { - mock.teardown(); - }); - - it('fetches a single error group', async () => { - mock = await inspectSearchParams(({ mockApmEventClient }) => - getErrorGroupSample({ - groupId: 'groupId', - serviceName: 'serviceName', - apmEventClient: mockApmEventClient, - environment: ENVIRONMENT_ALL.value, - kuery: '', - start: 0, - end: 50000, - }) - ); - - expect(mock.params).toMatchSnapshot(); - }); - - it('fetches multiple error groups', async () => { - mock = await inspectSearchParams(({ mockApmEventClient }) => - getErrorGroupMainStatistics({ - sortDirection: 'asc', - sortField: 'foo', - serviceName: 'serviceName', - apmEventClient: mockApmEventClient, - environment: ENVIRONMENT_ALL.value, - kuery: '', - start: 0, - end: 50000, - }) - ); - - expect(mock.params).toMatchSnapshot(); - }); - - it('fetches multiple error groups when sortField = lastSeen', async () => { - mock = await inspectSearchParams(({ mockApmEventClient }) => - getErrorGroupMainStatistics({ - sortDirection: 'asc', - sortField: 'lastSeen', - serviceName: 'serviceName', - apmEventClient: mockApmEventClient, - environment: ENVIRONMENT_ALL.value, - kuery: '', - start: 0, - end: 50000, - }) - ); - - expect(mock.params).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/apm/server/routes/errors/route.ts b/x-pack/plugins/apm/server/routes/errors/route.ts index 5bb3bda954ee..8a75ee69a41c 100644 --- a/x-pack/plugins/apm/server/routes/errors/route.ts +++ b/x-pack/plugins/apm/server/routes/errors/route.ts @@ -12,7 +12,8 @@ import { getErrorDistribution } from './distribution/get_distribution'; import { environmentRt, kueryRt, rangeRt } from '../default_api_types'; import { getErrorGroupMainStatistics } from './get_error_groups/get_error_group_main_statistics'; import { getErrorGroupPeriods } from './get_error_groups/get_error_group_detailed_statistics'; -import { getErrorGroupSample } from './get_error_groups/get_error_group_sample'; +import { getErrorGroupSampleIds } from './get_error_groups/get_error_group_sample_ids'; +import { getErrorSampleDetails } from './get_error_groups/get_error_sample_details'; import { offsetRt } from '../../../common/comparison_rt'; import { getTopErroneousTransactionsPeriods } from './erroneous_transactions/get_top_erroneous_transactions'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; @@ -187,8 +188,8 @@ const errorsDetailedStatisticsRoute = createApmServerRoute({ }, }); -const errorGroupsRoute = createApmServerRoute({ - endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}', +const errorGroupsSamplesRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples', params: t.type({ path: t.type({ serviceName: t.string, @@ -197,6 +198,41 @@ const errorGroupsRoute = createApmServerRoute({ query: t.intersection([environmentRt, kueryRt, rangeRt]), }), options: { tags: ['access:apm'] }, + handler: async ( + resources + ): Promise<{ + errorSampleIds: string[]; + occurrencesCount: number; + }> => { + const { params } = resources; + const apmEventClient = await getApmEventClient(resources); + const { serviceName, groupId } = params.path; + const { environment, kuery, start, end } = params.query; + + return getErrorGroupSampleIds({ + environment, + groupId, + kuery, + serviceName, + apmEventClient, + start, + end, + }); + }, +}); + +const errorGroupSampleDetailsRoute = createApmServerRoute({ + endpoint: + 'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}', + params: t.type({ + path: t.type({ + serviceName: t.string, + groupId: t.string, + errorId: t.string, + }), + query: t.intersection([environmentRt, kueryRt, rangeRt]), + }), + options: { tags: ['access:apm'] }, handler: async ( resources ): Promise<{ @@ -204,16 +240,15 @@ const errorGroupsRoute = createApmServerRoute({ | import('./../../../typings/es_schemas/ui/transaction').Transaction | undefined; error: import('./../../../typings/es_schemas/ui/apm_error').APMError; - occurrencesCount: number; }> => { const { params } = resources; const apmEventClient = await getApmEventClient(resources); - const { serviceName, groupId } = params.path; + const { serviceName, errorId } = params.path; const { environment, kuery, start, end } = params.query; - return getErrorGroupSample({ + return getErrorSampleDetails({ environment, - groupId, + errorId, kuery, serviceName, apmEventClient, @@ -323,7 +358,8 @@ export const errorsRouteRepository = { ...errorsMainStatisticsRoute, ...errorsMainStatisticsByTransactionNameRoute, ...errorsDetailedStatisticsRoute, - ...errorGroupsRoute, + ...errorGroupsSamplesRoute, + ...errorGroupSampleDetailsRoute, ...errorDistributionRoute, ...topErroneousTransactionsRoute, }; diff --git a/x-pack/plugins/apm/typings/es_schemas/raw/error_raw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/error_raw.ts index 8243c2b53a4f..7534c2f82066 100644 --- a/x-pack/plugins/apm/typings/es_schemas/raw/error_raw.ts +++ b/x-pack/plugins/apm/typings/es_schemas/raw/error_raw.ts @@ -35,7 +35,7 @@ export interface Exception { stacktrace?: Stackframe[]; } -interface Log { +export interface Log { message: string; stacktrace?: Stackframe[]; } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index b5484f5a56e8..039dba637b27 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -6763,7 +6763,6 @@ "xpack.apm.durationDistributionChart.totalTransactionsCount": "Total : {totalDocCount} {totalDocCount, plural, one {transaction} other {transactions}}", "xpack.apm.durationDistributionChartWithScrubber.selectionText": "Selection : {formattedSelection}", "xpack.apm.errorGroupDetails.errorGroupTitle": "Groupe d'erreurs {errorGroupId}", - "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "Visualiser {occurrencesCount} {occurrencesCount, plural, one {l'occurrence} other {les occurrences}} dans Discover", "xpack.apm.errorGroupTopTransactions.column.occurrences.valueLabel": "{occurrences} occ.", "xpack.apm.errorsTable.occurrences": "{occurrences} occ.", "xpack.apm.exactTransactionRateLabel": "{value} tpm", @@ -7235,14 +7234,10 @@ "xpack.apm.errorCountRuleType.errors": " erreurs", "xpack.apm.errorGroup.chart.ocurrences": "Occurrences", "xpack.apm.errorGroupDetails.culpritLabel": "Coupable", - "xpack.apm.errorGroupDetails.errorOccurrenceTitle": "Occurrence d'erreur", "xpack.apm.errorGroupDetails.exceptionMessageLabel": "Message d'exception", "xpack.apm.errorGroupDetails.logMessageLabel": "Message log", "xpack.apm.errorGroupDetails.occurrencesChartLabel": "Occurrences", - "xpack.apm.errorGroupDetails.occurrencesLabel": "Occurrences", - "xpack.apm.errorGroupDetails.relatedTransactionSample": "Échantillon de transaction associée", "xpack.apm.errorGroupDetails.unhandledLabel": "Non géré", - "xpack.apm.errorGroupDetails.viewOccurrencesInTraceExplorer": "Explorer les traces ayant cette erreur", "xpack.apm.errorGroupTopTransactions.column.occurrences": "Occurrences d'erreurs", "xpack.apm.errorGroupTopTransactions.column.transactionName": "Nom de la transaction", "xpack.apm.errorGroupTopTransactions.errorMessage": "Impossible de récupérer", @@ -7501,10 +7496,8 @@ "xpack.apm.notAvailableLabel": "S. O.", "xpack.apm.percentOfParent": "({value} de {parentType, select, transaction { transaction } trace {trace} })", "xpack.apm.propertiesTable.agentFeature.noDataAvailableLabel": "Aucune donnée disponible", - "xpack.apm.propertiesTable.tabs.exceptionStacktraceLabel": "Trace de pile d'exception", "xpack.apm.propertiesTable.tabs.logs.serviceName": "Nom de service", "xpack.apm.propertiesTable.tabs.logsLabel": "Logs", - "xpack.apm.propertiesTable.tabs.logStacktraceLabel": "Trace de pile des logs", "xpack.apm.propertiesTable.tabs.metadataLabel": "Métadonnées", "xpack.apm.propertiesTable.tabs.spanLinks": "Liens d'intervalle", "xpack.apm.propertiesTable.tabs.timelineLabel": "Chronologie", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 95ff0e1bcd85..f9e09c589ef8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6754,7 +6754,6 @@ "xpack.apm.durationDistributionChart.totalTransactionsCount": "{totalDocCount}合計{totalDocCount, plural, other {個のトランザクション}}", "xpack.apm.durationDistributionChartWithScrubber.selectionText": "選択:{formattedSelection}", "xpack.apm.errorGroupDetails.errorGroupTitle": "エラーグループ {errorGroupId}", - "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "Discoverで{occurrencesCount} {occurrencesCount, plural, other {件の発生}}を表示", "xpack.apm.errorGroupTopTransactions.column.occurrences.valueLabel": "{occurrences}件", "xpack.apm.errorsTable.occurrences": "{occurrences}件", "xpack.apm.exactTransactionRateLabel": "{value} tpm", @@ -7225,14 +7224,10 @@ "xpack.apm.errorCountRuleType.errors": " エラー", "xpack.apm.errorGroup.chart.ocurrences": "オカレンス", "xpack.apm.errorGroupDetails.culpritLabel": "原因", - "xpack.apm.errorGroupDetails.errorOccurrenceTitle": "エラーのオカレンス", "xpack.apm.errorGroupDetails.exceptionMessageLabel": "例外メッセージ", "xpack.apm.errorGroupDetails.logMessageLabel": "ログメッセージ", "xpack.apm.errorGroupDetails.occurrencesChartLabel": "オカレンス", - "xpack.apm.errorGroupDetails.occurrencesLabel": "オカレンス", - "xpack.apm.errorGroupDetails.relatedTransactionSample": "関連トランザクションサンプル", "xpack.apm.errorGroupDetails.unhandledLabel": "未対応", - "xpack.apm.errorGroupDetails.viewOccurrencesInTraceExplorer": "このエラーのトレースを調査", "xpack.apm.errorGroupTopTransactions.column.occurrences": "エラーのオカレンス", "xpack.apm.errorGroupTopTransactions.column.transactionName": "トランザクション名", "xpack.apm.errorGroupTopTransactions.errorMessage": "取得できませんでした", @@ -7490,10 +7485,8 @@ "xpack.apm.noDataConfig.solutionName": "Observability", "xpack.apm.notAvailableLabel": "N/A", "xpack.apm.propertiesTable.agentFeature.noDataAvailableLabel": "利用可能なデータがありません", - "xpack.apm.propertiesTable.tabs.exceptionStacktraceLabel": "例外のスタックトレース", "xpack.apm.propertiesTable.tabs.logs.serviceName": "サービス名", "xpack.apm.propertiesTable.tabs.logsLabel": "ログ", - "xpack.apm.propertiesTable.tabs.logStacktraceLabel": "スタックトレース", "xpack.apm.propertiesTable.tabs.metadataLabel": "メタデータ", "xpack.apm.propertiesTable.tabs.spanLinks": "スパンリンク", "xpack.apm.propertiesTable.tabs.timelineLabel": "Timeline", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4b4d96d9e049..ed44c4e0ea5d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6766,7 +6766,6 @@ "xpack.apm.durationDistributionChart.totalTransactionsCount": "共 {totalDocCount} 个{totalDocCount, plural, other {事务}}", "xpack.apm.durationDistributionChartWithScrubber.selectionText": "选择:{formattedSelection}", "xpack.apm.errorGroupDetails.errorGroupTitle": "错误组 {errorGroupId}", - "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "在 Discover 中查看 {occurrencesCount} 次{occurrencesCount, plural, other {发生}}", "xpack.apm.errorGroupTopTransactions.column.occurrences.valueLabel": "{occurrences} 次", "xpack.apm.errorsTable.occurrences": "{occurrences} 次", "xpack.apm.exactTransactionRateLabel": "{value} tpm", @@ -7238,14 +7237,10 @@ "xpack.apm.errorCountRuleType.errors": " 错误", "xpack.apm.errorGroup.chart.ocurrences": "发生次数", "xpack.apm.errorGroupDetails.culpritLabel": "原因", - "xpack.apm.errorGroupDetails.errorOccurrenceTitle": "错误发生", "xpack.apm.errorGroupDetails.exceptionMessageLabel": "异常消息", "xpack.apm.errorGroupDetails.logMessageLabel": "日志消息", "xpack.apm.errorGroupDetails.occurrencesChartLabel": "发生次数", - "xpack.apm.errorGroupDetails.occurrencesLabel": "发生次数", - "xpack.apm.errorGroupDetails.relatedTransactionSample": "相关的事务样本", "xpack.apm.errorGroupDetails.unhandledLabel": "未处理", - "xpack.apm.errorGroupDetails.viewOccurrencesInTraceExplorer": "浏览包含此错误的跟踪", "xpack.apm.errorGroupTopTransactions.column.occurrences": "错误发生次数", "xpack.apm.errorGroupTopTransactions.column.transactionName": "事务名称", "xpack.apm.errorGroupTopTransactions.errorMessage": "无法提取", @@ -7504,10 +7499,8 @@ "xpack.apm.notAvailableLabel": "不可用", "xpack.apm.percentOfParent": "({parentType, select, transaction {事务} trace {追溯} }的{value})", "xpack.apm.propertiesTable.agentFeature.noDataAvailableLabel": "没有可用数据", - "xpack.apm.propertiesTable.tabs.exceptionStacktraceLabel": "异常堆栈跟踪", "xpack.apm.propertiesTable.tabs.logs.serviceName": "服务名称", "xpack.apm.propertiesTable.tabs.logsLabel": "日志", - "xpack.apm.propertiesTable.tabs.logStacktraceLabel": "日志堆栈跟踪", "xpack.apm.propertiesTable.tabs.metadataLabel": "元数据", "xpack.apm.propertiesTable.tabs.spanLinks": "跨度链接", "xpack.apm.propertiesTable.tabs.timelineLabel": "时间线", diff --git a/x-pack/test/apm_api_integration/tests/errors/group_id.spec.ts b/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts similarity index 52% rename from x-pack/test/apm_api_integration/tests/errors/group_id.spec.ts rename to x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts index 93965744c4ff..5d5569ef7c64 100644 --- a/x-pack/test/apm_api_integration/tests/errors/group_id.spec.ts +++ b/x-pack/test/apm_api_integration/tests/errors/group_id_samples.spec.ts @@ -13,8 +13,11 @@ import { RecursivePartial } from '@kbn/apm-plugin/typings/common'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { config, generateData } from './generate_data'; -type ErrorsDistribution = - APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}'>; +type ErrorGroupSamples = + APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples'>; + +type ErrorSampleDetails = + APIReturnType<'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}'>; export default function ApiTest({ getService }: FtrProviderContext) { const registry = getService('registry'); @@ -25,13 +28,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { const start = new Date('2021-01-01T00:00:00.000Z').getTime(); const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; - async function callApi( + async function callErrorGroupSamplesApi( overrides?: RecursivePartial< - APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/errors/{groupId}'>['params'] + APIClientRequestParamsOf<'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples'>['params'] > ) { const response = await apmApiClient.readUser({ - endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}', + endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/samples', params: { path: { serviceName, @@ -50,15 +53,35 @@ export default function ApiTest({ getService }: FtrProviderContext) { return response; } + async function callErrorSampleDetailsApi(errorId: string) { + const response = await apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}', + params: { + path: { + serviceName, + groupId: 'foo', + errorId, + }, + query: { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + environment: 'ENVIRONMENT_ALL', + kuery: '', + }, + }, + }); + return response; + } + registry.when('when data is not loaded', { config: 'basic', archives: [] }, () => { it('handles the empty state', async () => { - const response = await callApi(); + const response = await callErrorGroupSamplesApi(); expect(response.status).to.be(200); expect(response.body.occurrencesCount).to.be(0); }); }); - registry.when('when data is loaded', { config: 'basic', archives: [] }, () => { + registry.when('when samples data is loaded', { config: 'basic', archives: [] }, () => { const { bananaTransaction } = config; describe('error group id', () => { before(async () => { @@ -68,21 +91,52 @@ export default function ApiTest({ getService }: FtrProviderContext) { after(() => synthtraceEsClient.clean()); describe('return correct data', () => { - let errorsDistribution: ErrorsDistribution; + let errorsSamplesResponse: ErrorGroupSamples; before(async () => { - const response = await callApi({ + const response = await callErrorGroupSamplesApi({ path: { groupId: '0000000000000000000000000Error 1' }, }); - errorsDistribution = response.body; + errorsSamplesResponse = response.body; }); it('displays correct number of occurrences', () => { const numberOfBuckets = 15; - expect(errorsDistribution.occurrencesCount).to.equal( + expect(errorsSamplesResponse.occurrencesCount).to.equal( bananaTransaction.failureRate * numberOfBuckets ); }); }); }); }); + + registry.when('when error sample data is loaded', { config: 'basic', archives: [] }, () => { + describe('error sample id', () => { + before(async () => { + await generateData({ serviceName, start, end, synthtraceEsClient }); + }); + + after(() => synthtraceEsClient.clean()); + + describe('return correct data', () => { + let errorSampleDetailsResponse: ErrorSampleDetails; + before(async () => { + const errorsSamplesResponse = await callErrorGroupSamplesApi({ + path: { groupId: '0000000000000000000000000Error 1' }, + }); + + const errorId = errorsSamplesResponse.body.errorSampleIds[0]; + + const response = await callErrorSampleDetailsApi(errorId); + errorSampleDetailsResponse = response.body; + }); + + it('displays correct error sample data', () => { + expect(errorSampleDetailsResponse.error.error.grouping_key).to.equal( + '0000000000000000000000000Error 1' + ); + expect(errorSampleDetailsResponse.error.error.exception?.[0].message).to.equal('Error 1'); + }); + }); + }); + }); } diff --git a/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts b/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts index 3d1961c3f8cb..c68c91d858f7 100644 --- a/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.spec.ts @@ -75,7 +75,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }, { req: { - url: `/internal/apm/services/foo/errors/bar?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, + url: `/internal/apm/services/foo/errors/bar/samples?start=${start}&end=${end}&environment=ENVIRONMENT_ALL&kuery=`, }, expectForbidden: expect403, expectResponse: expect200, From 1d0a8c9ddd47fc6b9a70e3b46bb15ef0553ce18f Mon Sep 17 00:00:00 2001 From: Katerina Patticha Date: Tue, 3 Jan 2023 17:57:16 +0100 Subject: [PATCH 15/30] [APM] Enable Operations Breakdown by default (#148305) ## Summary closes: https://github.com/elastic/kibana/issues/147682 https://user-images.githubusercontent.com/3369346/210387189-41423215-b17e-49b3-a84b-4e080ad4fc5b.mov 1. Enables Operations Breakdown 2. Removes the advanced setting for the operations 3. Passes the params --- .../server/collectors/management/schema.ts | 4 -- .../server/collectors/management/types.ts | 1 - src/plugins/telemetry/schema/oss_plugins.json | 6 -- .../waterfall/flyout_top_level_properties.tsx | 3 +- .../waterfall/waterfall_flyout.tsx | 3 +- .../components/routing/home/dependencies.tsx | 1 + .../templates/dependency_detail_template.tsx | 56 +++++++++---------- ...se_operations_breakdown_enabled_setting.ts | 15 ----- x-pack/plugins/observability/common/index.ts | 1 - .../observability/common/ui_settings_keys.ts | 1 - .../observability/server/ui_settings.ts | 24 -------- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 14 files changed, 30 insertions(+), 88 deletions(-) delete mode 100644 x-pack/plugins/apm/public/hooks/use_operations_breakdown_enabled_setting.ts diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 25a943496ab8..72cb43d7734a 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -526,10 +526,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'keyword', _meta: { description: 'Default value of the setting was changed.' }, }, - 'observability:apmOperationsTab': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'observability:apmLabsButton': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index a7bb32f53bd1..d3832cc96eeb 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -142,7 +142,6 @@ export interface UsageStats { isDefaultIndexMigrated: boolean; 'lens:useFieldExistenceSampling': boolean; 'metrics:allowCheckingForFailedShards': boolean; - 'observability:apmOperationsTab': boolean; 'observability:apmLabsButton': boolean; 'observability:enableAwsLambdaMetrics': boolean; 'observability:apmProgressiveLoading': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index af8e3d3fe7dc..f348181a1e29 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -9048,12 +9048,6 @@ "description": "Default value of the setting was changed." } }, - "observability:apmOperationsTab": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "observability:apmLabsButton": { "type": "boolean", "_meta": { diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx index 23c06c586def..580e2b3568d0 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/flyout_top_level_properties.tsx @@ -27,7 +27,8 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { const { query } = useAnyOfApmParams( '/services/{serviceName}/transactions/view', '/mobile-services/{serviceName}/transactions/view', - '/traces/explorer' + '/traces/explorer', + '/dependencies/operation' ); const latencyAggregationType = diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_flyout.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_flyout.tsx index 5273caa3c8df..10333af97c6d 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_flyout.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_flyout.tsx @@ -35,7 +35,8 @@ export function WaterfallFlyout({ query: { flyoutDetailTab }, } = useAnyOfApmParams( '/services/{serviceName}/transactions/view', - '/mobile-services/{serviceName}/transactions/view' + '/mobile-services/{serviceName}/transactions/view', + '/dependencies/operation' ); const currentItem = waterfall.items.find( (item) => item.id === waterfallItemId diff --git a/x-pack/plugins/apm/public/components/routing/home/dependencies.tsx b/x-pack/plugins/apm/public/components/routing/home/dependencies.tsx index f22960dd5076..0206d310737c 100644 --- a/x-pack/plugins/apm/public/components/routing/home/dependencies.tsx +++ b/x-pack/plugins/apm/public/components/routing/home/dependencies.tsx @@ -86,6 +86,7 @@ export const dependencies = { sampleRangeFrom: toNumberRt, sampleRangeTo: toNumberRt, waterfallItemId: t.string, + flyoutDetailTab: t.string, }), ]), }), diff --git a/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx index 833b489004bf..605936dd5794 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/dependency_detail_template.tsx @@ -16,7 +16,6 @@ import { useApmParams } from '../../../hooks/use_apm_params'; import { useApmRouter } from '../../../hooks/use_apm_router'; import { useApmRoutePath } from '../../../hooks/use_apm_route_path'; import { useFetcher } from '../../../hooks/use_fetcher'; -import { useOperationBreakdownEnabledSetting } from '../../../hooks/use_operations_breakdown_enabled_setting'; import { useTimeRange } from '../../../hooks/use_time_range'; import { BetaBadge } from '../../shared/beta_badge'; import { SearchBar } from '../../shared/search_bar'; @@ -39,9 +38,6 @@ export function DependencyDetailTemplate({ children }: Props) { const path = useApmRoutePath(); - const isOperationsBreakdownFeatureEnabled = - useOperationBreakdownEnabledSetting(); - const kueryBarBoolFilter = getKueryBarBoolFilter({ environment, dependencyName, @@ -68,33 +64,31 @@ export function DependencyDetailTemplate({ children }: Props) { const { data: { metadata } = {} } = dependencyMetadataFetch; - const tabs = isOperationsBreakdownFeatureEnabled - ? [ - { - key: 'overview', - href: router.link('/dependencies/overview', { - query, - }), - label: i18n.translate('xpack.apm.DependencyDetailOverview.title', { - defaultMessage: 'Overview', - }), - isSelected: path === '/dependencies/overview', - }, - { - key: 'operations', - href: router.link('/dependencies/operations', { - query, - }), - label: i18n.translate('xpack.apm.DependencyDetailOperations.title', { - defaultMessage: 'Operations', - }), - isSelected: - path === '/dependencies/operations' || - path === '/dependencies/operation', - append: , - }, - ] - : []; + const tabs = [ + { + key: 'overview', + href: router.link('/dependencies/overview', { + query, + }), + label: i18n.translate('xpack.apm.DependencyDetailOverview.title', { + defaultMessage: 'Overview', + }), + isSelected: path === '/dependencies/overview', + }, + { + key: 'operations', + href: router.link('/dependencies/operations', { + query, + }), + label: i18n.translate('xpack.apm.DependencyDetailOperations.title', { + defaultMessage: 'Operations', + }), + isSelected: + path === '/dependencies/operations' || + path === '/dependencies/operation', + append: , + }, + ]; return ( (apmOperationsTab, false); -} diff --git a/x-pack/plugins/observability/common/index.ts b/x-pack/plugins/observability/common/index.ts index 14da44c47039..86104ab47c07 100644 --- a/x-pack/plugins/observability/common/index.ts +++ b/x-pack/plugins/observability/common/index.ts @@ -21,7 +21,6 @@ export { apmServiceInventoryOptimizedSorting, apmServiceGroupMaxNumberOfServices, apmTraceExplorerTab, - apmOperationsTab, apmLabsButton, enableInfrastructureHostsView, enableAwsLambdaMetrics, diff --git a/x-pack/plugins/observability/common/ui_settings_keys.ts b/x-pack/plugins/observability/common/ui_settings_keys.ts index 6961c8496038..8ff913a0d658 100644 --- a/x-pack/plugins/observability/common/ui_settings_keys.ts +++ b/x-pack/plugins/observability/common/ui_settings_keys.ts @@ -16,7 +16,6 @@ export const apmServiceInventoryOptimizedSorting = export const apmServiceGroupMaxNumberOfServices = 'observability:apmServiceGroupMaxNumberOfServices'; export const apmTraceExplorerTab = 'observability:apmTraceExplorerTab'; -export const apmOperationsTab = 'observability:apmOperationsTab'; export const apmLabsButton = 'observability:apmLabsButton'; export const enableInfrastructureHostsView = 'observability:enableInfrastructureHostsView'; export const enableAwsLambdaMetrics = 'observability:enableAwsLambdaMetrics'; diff --git a/x-pack/plugins/observability/server/ui_settings.ts b/x-pack/plugins/observability/server/ui_settings.ts index a9c580726997..89bf6f9bb382 100644 --- a/x-pack/plugins/observability/server/ui_settings.ts +++ b/x-pack/plugins/observability/server/ui_settings.ts @@ -19,7 +19,6 @@ import { enableNewSyntheticsView, apmServiceGroupMaxNumberOfServices, apmTraceExplorerTab, - apmOperationsTab, apmLabsButton, enableAgentExplorerView, enableAwsLambdaMetrics, @@ -29,10 +28,6 @@ import { enableInfrastructureHostsView, } from '../common/ui_settings_keys'; -const betaLabel = i18n.translate('xpack.observability.uiSettings.betaLabel', { - defaultMessage: 'beta', -}); - const technicalPreviewLabel = i18n.translate( 'xpack.observability.uiSettings.technicalPreviewLabel', { defaultMessage: 'technical preview' } @@ -220,25 +215,6 @@ export const uiSettings: Record = { type: 'boolean', showInLabs: true, }, - [apmOperationsTab]: { - category: [observabilityFeatureId], - name: i18n.translate('xpack.observability.apmOperationsBreakdown', { - defaultMessage: 'APM Operations Breakdown', - }), - description: i18n.translate('xpack.observability.apmOperationsBreakdownDescription', { - defaultMessage: - '{betaLabel} Enable the APM Operations Breakdown feature, that displays aggregates for backend operations. {feedbackLink}.', - values: { - betaLabel: `[${betaLabel}]`, - feedbackLink: feedbackLink({ href: 'https://ela.st/feedback-operations-breakdown' }), - }, - }), - schema: schema.boolean(), - value: true, - requiresPageReload: true, - type: 'boolean', - showInLabs: false, - }, [apmLabsButton]: { category: [observabilityFeatureId], name: i18n.translate('xpack.observability.apmLabs', { diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 039dba637b27..ca749484c24c 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -24615,7 +24615,6 @@ "xpack.observability.apmAWSLambdaRequestCostPerMillion": "Prix AWS Lambda pour 1M de requêtes", "xpack.observability.apmLabs": "Activer le bouton Ateliers dans APM", "xpack.observability.apmLabsDescription": "Cet indicateur détermine si l'utilisateur a accès au bouton Ateliers, moyen rapide d'activer et de désactiver les fonctionnalités de la version d'évaluation technique dans APM.", - "xpack.observability.apmOperationsBreakdown": "Répartition des opérations APM", "xpack.observability.apmProgressiveLoading": "Utiliser le chargement progressif des vues APM sélectionnées", "xpack.observability.apmProgressiveLoadingQualityHigh": "Taux d'échantillonnage élevé (plus lent, plus précis)", "xpack.observability.apmProgressiveLoadingQualityLow": "Taux d'échantillonnage bas (plus rapide, moins précis)", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f9e09c589ef8..e41d15377aa4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24594,7 +24594,6 @@ "xpack.observability.apmAWSLambdaRequestCostPerMillion": "1MリクエストごとのAWS Lambdaの料金", "xpack.observability.apmLabs": "APMで[ラボ]ボタンを有効にする", "xpack.observability.apmLabsDescription": "このフラグはビューアーで[ラボ]ボタンを使用できるかどうかを決定します。APMでテクニカルプレビュー中の機能を有効および無効にするための簡単な方法です。", - "xpack.observability.apmOperationsBreakdown": "APM演算内訳", "xpack.observability.apmProgressiveLoading": "選択したAPMビューのプログレッシブ読み込みを使用", "xpack.observability.apmProgressiveLoadingQualityHigh": "高サンプリングレート(低速、最も精度が高い)", "xpack.observability.apmProgressiveLoadingQualityLow": "低サンプリングレート(最速、最も精度が低い)", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ed44c4e0ea5d..efc9c2e106f8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24624,7 +24624,6 @@ "xpack.observability.apmAWSLambdaRequestCostPerMillion": "AWS lambda 每 1M 请求的价格", "xpack.observability.apmLabs": "在 APM 中启用“实验”按钮", "xpack.observability.apmLabsDescription": "此标志决定查看者是否有权访问用于在 APM 中快速启用和禁用技术预览功能的“实验”按钮。", - "xpack.observability.apmOperationsBreakdown": "APM Operations Breakdown", "xpack.observability.apmProgressiveLoading": "使用渐进方式加载选定 APM 视图", "xpack.observability.apmProgressiveLoadingQualityHigh": "高采样速率(更慢,最准确)", "xpack.observability.apmProgressiveLoadingQualityLow": "低采样速率(最快,最不准确)", From ee6170be7a89551a60e5d6f82b733cdb2a5c7a79 Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Tue, 3 Jan 2023 17:17:33 +0000 Subject: [PATCH 16/30] Include client IP address in audit log (#148055) Follow up to #147526 which had to be reverted. Resolves #127481 ## Release notes Include IP address in audit log ## Testing 1. Start Elasticsearch with trial license: `yarn es snapshot --license trial` 2. Update `kibana.dev.yaml`: ```yaml xpack.security.audit.enabled: true xpack.security.audit.appender: type: console layout: type: json ``` 3. Observe audit logs in console when interacting with Kibana: ```json { "@timestamp": "2022-12-13T15:50:42.236+00:00", "message": "User is requesting [/dev/internal/security/me] endpoint", "client": { "ip": "127.0.0.1" }, "http": { "request": { "headers": { "x-forwarded-for": "1.1.1.1, 127.0.0.1" } } } } ``` Note: You will see the `x-forwarded-for` field populated when running Kibana in development mode (`yarn start`) since Kibana runs behind a development proxy. Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- docs/user/security/audit-logging.asciidoc | 8 ++ .../src/socket.test.ts | 7 + .../src/socket.ts | 4 + .../core-http-server/src/router/socket.ts | 7 + .../security/server/audit/audit_events.ts | 126 ++++++++++++------ .../server/audit/audit_service.test.ts | 95 ++++++++++++- .../security/server/audit/audit_service.ts | 27 ++++ x-pack/plugins/security/server/audit/index.ts | 2 +- .../server/authentication/api_keys/index.ts | 1 + .../security/server/authentication/index.ts | 1 + x-pack/plugins/security/server/index.ts | 3 +- .../tests/audit/audit_log.ts | 6 + 12 files changed, 240 insertions(+), 47 deletions(-) diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index 5f6fe746814e..b83eea1dc531 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -407,11 +407,19 @@ Example: `[marketing]` | *Field* | *Description* +| `client.ip` +| Client IP address. + | `http.request.method` | HTTP request method. Example: `get`, `post`, `put`, `delete` +| `http.request.headers.x-forwarded-for` +| `X-Forwarded-For` request header used to identify the originating client IP address when connecting through proxy servers. + +Example: `161.66.20.177, 236.198.214.101` + | `url.domain` | Domain of the URL. diff --git a/packages/core/http/core-http-router-server-internal/src/socket.test.ts b/packages/core/http/core-http-router-server-internal/src/socket.test.ts index 85a0198c3e6a..b4ccfb37b140 100644 --- a/packages/core/http/core-http-router-server-internal/src/socket.test.ts +++ b/packages/core/http/core-http-router-server-internal/src/socket.test.ts @@ -153,6 +153,13 @@ describe('KibanaSocket', () => { }); }); + describe('remoteAddress', () => { + it('mirrors the value of net.Socket instance', () => { + const socket = new KibanaSocket({ remoteAddress: '1.1.1.1' } as Socket); + expect(socket.remoteAddress).toBe('1.1.1.1'); + }); + }); + describe('getFakeSocket', () => { it('returns a stub', async () => { const fakeSocket = KibanaSocket.getFakeSocket(); diff --git a/packages/core/http/core-http-router-server-internal/src/socket.ts b/packages/core/http/core-http-router-server-internal/src/socket.ts index b62e527fa6c0..eaa6e18570a0 100644 --- a/packages/core/http/core-http-router-server-internal/src/socket.ts +++ b/packages/core/http/core-http-router-server-internal/src/socket.ts @@ -28,6 +28,10 @@ export class KibanaSocket implements IKibanaSocket { return this.socket instanceof TLSSocket ? this.socket.authorizationError : undefined; } + public get remoteAddress() { + return this.socket.remoteAddress; + } + constructor(private readonly socket: Socket) {} getPeerCertificate(detailed: true): DetailedPeerCertificate | null; diff --git a/packages/core/http/core-http-server/src/router/socket.ts b/packages/core/http/core-http-server/src/router/socket.ts index cc47373a583f..b4c53a95daab 100644 --- a/packages/core/http/core-http-server/src/router/socket.ts +++ b/packages/core/http/core-http-server/src/router/socket.ts @@ -51,4 +51,11 @@ export interface IKibanaSocket { * only when `authorized` is `false`. */ readonly authorizationError?: Error; + + /** + * The string representation of the remote IP address. For example,`'74.125.127.100'` or + * `'2001:4860:a005::68'`. Value may be `undefined` if the socket is destroyed (for example, if + * the client disconnected). + */ + readonly remoteAddress?: string; } diff --git a/x-pack/plugins/security/server/audit/audit_events.ts b/x-pack/plugins/security/server/audit/audit_events.ts index 5e38d5d53f22..0ba0530b0458 100644 --- a/x-pack/plugins/security/server/audit/audit_events.ts +++ b/x-pack/plugins/security/server/audit/audit_events.ts @@ -14,6 +14,77 @@ import type { EcsEventOutcome, EcsEventType, KibanaRequest, LogMeta } from '@kbn import type { AuthenticationProvider } from '../../common/model'; import type { AuthenticationResult } from '../authentication/authentication_result'; +/** + * Audit kibana schema using ECS format + */ +export interface AuditKibana { + /** + * The ID of the space associated with this event. + */ + space_id?: string; + /** + * The ID of the user session associated with this event. Each login attempt + * results in a unique session id. + */ + session_id?: string; + /** + * Saved object that was created, changed, deleted or accessed as part of this event. + */ + saved_object?: { + type: string; + id: string; + }; + /** + * Name of authentication provider associated with a login event. + */ + authentication_provider?: string; + /** + * Type of authentication provider associated with a login event. + */ + authentication_type?: string; + /** + * Name of Elasticsearch realm that has authenticated the user. + */ + authentication_realm?: string; + /** + * Name of Elasticsearch realm where the user details were retrieved from. + */ + lookup_realm?: string; + /** + * Set of space IDs that a saved object was shared to. + */ + add_to_spaces?: readonly string[]; + /** + * Set of space IDs that a saved object was removed from. + */ + delete_from_spaces?: readonly string[]; +} + +type EcsHttp = Required['http']; +type EcsRequest = Required['request']; + +/** + * Audit request schema using ECS format + */ +export interface AuditRequest extends EcsRequest { + /** + * HTTP request headers + */ + headers?: { + 'x-forwarded-for'?: string; + }; +} + +/** + * Audit http schema using ECS format + */ +export interface AuditHttp extends EcsHttp { + /** + * HTTP request details + */ + request?: AuditRequest; +} + /** * Audit event schema using ECS format: https://www.elastic.co/guide/en/ecs/1.12/index.html * @@ -23,49 +94,20 @@ import type { AuthenticationResult } from '../authentication/authentication_resu * @public */ export interface AuditEvent extends LogMeta { + /** + * Log message + */ message: string; - kibana?: { - /** - * The ID of the space associated with this event. - */ - space_id?: string; - /** - * The ID of the user session associated with this event. Each login attempt - * results in a unique session id. - */ - session_id?: string; - /** - * Saved object that was created, changed, deleted or accessed as part of this event. - */ - saved_object?: { - type: string; - id: string; - }; - /** - * Name of authentication provider associated with a login event. - */ - authentication_provider?: string; - /** - * Type of authentication provider associated with a login event. - */ - authentication_type?: string; - /** - * Name of Elasticsearch realm that has authenticated the user. - */ - authentication_realm?: string; - /** - * Name of Elasticsearch realm where the user details were retrieved from. - */ - lookup_realm?: string; - /** - * Set of space IDs that a saved object was shared to. - */ - add_to_spaces?: readonly string[]; - /** - * Set of space IDs that a saved object was removed from. - */ - delete_from_spaces?: readonly string[]; - }; + + /** + * Kibana specific fields + */ + kibana?: AuditKibana; + + /** + * Fields describing an HTTP request + */ + http?: AuditHttp; } export interface HttpRequestParams { diff --git a/x-pack/plugins/security/server/audit/audit_service.test.ts b/x-pack/plugins/security/server/audit/audit_service.test.ts index 900da83a6eb2..a0d35bf80389 100644 --- a/x-pack/plugins/security/server/audit/audit_service.test.ts +++ b/x-pack/plugins/security/server/audit/audit_service.test.ts @@ -5,8 +5,11 @@ * 2.0. */ +import type { Socket } from 'net'; import { lastValueFrom, Observable, of } from 'rxjs'; +import type { FakeRawRequest } from '@kbn/core/server'; +import { CoreKibanaRequest } from '@kbn/core/server'; import { coreMock, httpServerMock, @@ -22,6 +25,7 @@ import { AuditService, createLoggingConfig, filterEvent, + getForwardedFor, RECORD_USAGE_INTERVAL, } from './audit_service'; @@ -173,7 +177,7 @@ describe('#setup', () => { }); describe('#asScoped', () => { - it('logs event enriched with meta data', async () => { + it('logs event enriched with meta data from request', async () => { const audit = new AuditService(logger); const auditSetup = audit.setup({ license, @@ -186,19 +190,78 @@ describe('#asScoped', () => { recordAuditLoggingUsage, }); const request = httpServerMock.createKibanaRequest({ + socket: { remoteAddress: '3.3.3.3' } as Socket, + headers: { + 'x-forwarded-for': '1.1.1.1, 2.2.2.2', + }, kibanaRequestState: { requestId: 'REQUEST_ID', requestUuid: 'REQUEST_UUID' }, }); - await auditSetup.asScoped(request).log({ message: 'MESSAGE', event: { action: 'ACTION' } }); - expect(logger.info).toHaveBeenCalledWith('MESSAGE', { + await auditSetup.asScoped(request).log({ + message: 'MESSAGE', + event: { action: 'ACTION' }, + http: { request: { method: 'GET' } }, + }); + expect(logger.info).toHaveBeenLastCalledWith('MESSAGE', { event: { action: 'ACTION' }, kibana: { space_id: 'default', session_id: 'SESSION_ID' }, trace: { id: 'REQUEST_ID' }, + client: { ip: '3.3.3.3' }, + http: { + request: { method: 'GET', headers: { 'x-forwarded-for': '1.1.1.1, 2.2.2.2' } }, + }, user: { id: 'uid', name: 'jdoe', roles: ['admin'] }, }); audit.stop(); }); + it('logs event enriched with meta data from fake request', async () => { + const audit = new AuditService(logger); + const auditSetup = audit.setup({ + license, + config, + logging, + http, + getCurrentUser, + getSpaceId: () => undefined, + getSID: () => Promise.resolve(undefined), + recordAuditLoggingUsage, + }); + + const fakeRawRequest: FakeRawRequest = { + headers: {}, + path: '/', + }; + const request = CoreKibanaRequest.from(fakeRawRequest); + + await auditSetup.asScoped(request).log({ + message: 'MESSAGE', + event: { action: 'ACTION' }, + }); + expect(logger.info).toHaveBeenLastCalledWith('MESSAGE', { + client: { + ip: undefined, + }, + event: { + action: 'ACTION', + }, + http: undefined, + kibana: { + session_id: undefined, + space_id: undefined, + }, + trace: { + id: expect.any(String), + }, + user: { + id: 'uid', + name: 'jdoe', + roles: ['admin'], + }, + }); + audit.stop(); + }); + it('does not log to audit logger if event matches ignore filter', async () => { const audit = new AuditService(logger); const auditSetup = audit.setup({ @@ -424,6 +487,32 @@ describe('#createLoggingConfig', () => { }); }); +describe('#getForwardedFor', () => { + it('extracts x-forwarded-for header from request', () => { + const request = httpServerMock.createKibanaRequest({ + headers: { + 'x-forwarded-for': '1.1.1.1', + }, + }); + expect(getForwardedFor(request)).toBe('1.1.1.1'); + }); + + it('concatenates multiple headers into single string in correct order', () => { + const request = httpServerMock.createKibanaRequest({ + headers: { + // @ts-expect-error Headers can be arrays but HAPI mocks are incorrectly typed + 'x-forwarded-for': ['1.1.1.1, 2.2.2.2', '3.3.3.3'], + }, + }); + expect(getForwardedFor(request)).toBe('1.1.1.1, 2.2.2.2, 3.3.3.3'); + }); + + it('returns undefined when header not present', () => { + const request = httpServerMock.createKibanaRequest(); + expect(getForwardedFor(request)).toBeUndefined(); + }); +}); + describe('#filterEvent', () => { let event: AuditEvent; diff --git a/x-pack/plugins/security/server/audit/audit_service.ts b/x-pack/plugins/security/server/audit/audit_service.ts index ff8a09df4019..a163a75e7187 100644 --- a/x-pack/plugins/security/server/audit/audit_service.ts +++ b/x-pack/plugins/security/server/audit/audit_service.ts @@ -162,6 +162,8 @@ export class AuditService { const spaceId = getSpaceId(request); const user = getCurrentUser(request); const sessionId = await getSID(request); + const forwardedFor = getForwardedFor(request); + log({ ...event, user: @@ -177,6 +179,18 @@ export class AuditService { ...event.kibana, }, trace: { id: request.id }, + client: { ip: request.socket.remoteAddress }, + http: forwardedFor + ? { + ...event.http, + request: { + ...event.http?.request, + headers: { + 'x-forwarded-for': forwardedFor, + }, + }, + } + : event.http, }); }, enabled, @@ -243,3 +257,16 @@ export function filterEvent( } return true; } + +/** + * Extracts `X-Forwarded-For` header(s) from `KibanaRequest`. + */ +export function getForwardedFor(request: KibanaRequest) { + const forwardedFor = request.headers['x-forwarded-for']; + + if (Array.isArray(forwardedFor)) { + return forwardedFor.join(', '); + } + + return forwardedFor; +} diff --git a/x-pack/plugins/security/server/audit/index.ts b/x-pack/plugins/security/server/audit/index.ts index c3cb5f890ce0..15d91168c6ac 100644 --- a/x-pack/plugins/security/server/audit/index.ts +++ b/x-pack/plugins/security/server/audit/index.ts @@ -7,7 +7,7 @@ export type { AuditServiceSetup, AuditLogger } from './audit_service'; export { AuditService } from './audit_service'; -export type { AuditEvent } from './audit_events'; +export type { AuditEvent, AuditHttp, AuditKibana, AuditRequest } from './audit_events'; export { userLoginEvent, userLogoutEvent, diff --git a/x-pack/plugins/security/server/authentication/api_keys/index.ts b/x-pack/plugins/security/server/authentication/api_keys/index.ts index c3ef6f6f139a..b93043302f8d 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/index.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/index.ts @@ -10,6 +10,7 @@ export type { InvalidateAPIKeyResult, CreateAPIKeyParams, InvalidateAPIKeysParams, + ValidateAPIKeyParams, GrantAPIKeyResult, } from './api_keys'; export { APIKeys, CreateApiKeyValidationError } from './api_keys'; diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index 23ec94100df8..2e844c6f6054 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -32,5 +32,6 @@ export type { InvalidateAPIKeyResult, CreateAPIKeyParams, InvalidateAPIKeysParams, + ValidateAPIKeyParams, GrantAPIKeyResult, } from './api_keys'; diff --git a/x-pack/plugins/security/server/index.ts b/x-pack/plugins/security/server/index.ts index 2b6a8805ad8e..2c000b9b0226 100644 --- a/x-pack/plugins/security/server/index.ts +++ b/x-pack/plugins/security/server/index.ts @@ -26,11 +26,12 @@ export type { InvalidateAPIKeysParams, InvalidateAPIKeyResult, GrantAPIKeyResult, + ValidateAPIKeyParams, AuthenticationServiceStart, } from './authentication'; export type { CheckPrivilegesPayload, CasesSupportedOperations } from './authorization'; export type AuthorizationServiceSetup = SecurityPluginStart['authz']; -export type { AuditLogger, AuditEvent } from './audit'; +export type { AuditLogger, AuditEvent, AuditHttp, AuditKibana, AuditRequest } from './audit'; export type { SecurityPluginSetup, SecurityPluginStart }; export type { AuthenticatedUser } from '../common/model'; export { ROUTE_TAG_CAN_REDIRECT } from './routes/tags'; diff --git a/x-pack/test/security_api_integration/tests/audit/audit_log.ts b/x-pack/test/security_api_integration/tests/audit/audit_log.ts index 553efa63431f..df4dd45be0e1 100644 --- a/x-pack/test/security_api_integration/tests/audit/audit_log.ts +++ b/x-pack/test/security_api_integration/tests/audit/audit_log.ts @@ -55,6 +55,7 @@ export default function ({ getService }: FtrProviderContext) { await supertest .post('/internal/security/login') .set('kbn-xsrf', 'xxx') + .set('X-Forwarded-For', '1.1.1.1, 2.2.2.2') .send({ providerType: 'basic', providerName: 'basic', @@ -71,12 +72,15 @@ export default function ({ getService }: FtrProviderContext) { expect(loginEvent.event.outcome).to.be('success'); expect(loginEvent.trace.id).to.be.ok(); expect(loginEvent.user.name).to.be(username); + expect(loginEvent.client.ip).to.be.ok(); + expect(loginEvent.http.request.headers['x-forwarded-for']).to.be('1.1.1.1, 2.2.2.2'); }); it('logs audit events when failing to log in', async () => { await supertest .post('/internal/security/login') .set('kbn-xsrf', 'xxx') + .set('X-Forwarded-For', '1.1.1.1, 2.2.2.2') .send({ providerType: 'basic', providerName: 'basic', @@ -93,6 +97,8 @@ export default function ({ getService }: FtrProviderContext) { expect(loginEvent.event.outcome).to.be('failure'); expect(loginEvent.trace.id).to.be.ok(); expect(loginEvent.user).not.to.be.ok(); + expect(loginEvent.client.ip).to.be.ok(); + expect(loginEvent.http.request.headers['x-forwarded-for']).to.be('1.1.1.1, 2.2.2.2'); }); }); } From 51b11c0fb8f3f1a9f879bde6ea11d668e1523b35 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Tue, 3 Jan 2023 13:00:21 -0500 Subject: [PATCH 17/30] [Dashboard][Portable Dashboards] Unskip Unsaved Listing Tests (#147820) ## Summary Fixes a race condition that resulted in CI flakiness when checking for Dashboard unsaved changes: --- .../embeddable/dashboard_container.tsx | 7 +- .../dashboard_diffing_integration.ts | 115 ++++++++---------- .../group1/dashboard_unsaved_listing.ts | 6 +- .../functional/page_objects/dashboard_page.ts | 6 + 4 files changed, 62 insertions(+), 72 deletions(-) diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx index f1654242fd88..4270747fd9d1 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx @@ -283,10 +283,7 @@ export class DashboardContainer extends Container - setTimeout( - () => this.addNewEmbeddable(incomingEmbeddable.type, incomingEmbeddable.input), - 1 // add embeddable on next update so that the state diff can pick it up. - ) + this.addNewEmbeddable(incomingEmbeddable.type, incomingEmbeddable.input) ); } } @@ -315,8 +312,6 @@ export class DashboardContainer extends Container { this.stopDiffingDashboardState = cleanup; diff --git a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts index 281d8f397bc2..32956cea18d1 100644 --- a/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts +++ b/src/plugins/dashboard/public/dashboard_container/embeddable/integrations/diff_state/dashboard_diffing_integration.ts @@ -61,35 +61,13 @@ export const keysNotConsideredUnsavedChanges: Array void) => void; } ) { - const { dashboardSessionStorage } = pluginServices.getServices(); - - const checkForUnsavedChanges = async ( - lastState: DashboardContainerByValueInput, - currentState: DashboardContainerByValueInput - ): Promise => { - const unsavedChanges = await getUnsavedChanges.bind(this)(lastState, currentState); - - if (useSessionBackup) { - dashboardSessionStorage.setState( - this.getDashboardSavedObjectId(), - omit(unsavedChanges, keysToOmitFromSessionStorage) - ); - } - - return Object.keys(omit(unsavedChanges, keysNotConsideredUnsavedChanges)).length > 0; // omit view mode because it is always backed up - }; - const checkForUnsavedChangesSubject$ = new Subject(); // middleware starts the check for unsaved changes function if the action dispatched could cause them. @@ -105,53 +83,61 @@ export function startDiffingDashboardState( next(action); }; - // once the dashboard is initialized, start listening to the subject - this.untilInitialized().then(() => { - const { - getState, - dispatch, - actions: { setHasUnsavedChanges }, - } = this.getReduxEmbeddableTools(); - - const getHasUnsavedChangesSubscription = checkForUnsavedChangesSubject$ - .pipe( - debounceTime(CHANGE_CHECK_DEBOUNCE), - switchMap(() => { - return new Observable((observer) => { - const { - explicitInput: currentInput, - componentState: { lastSavedInput }, - } = this.getReduxEmbeddableTools().getState(); - checkForUnsavedChanges(lastSavedInput, currentInput).then((hasChanges) => { + const getHasUnsavedChangesSubscription = checkForUnsavedChangesSubject$ + .pipe( + debounceTime(CHANGE_CHECK_DEBOUNCE), + switchMap(() => { + return new Observable((observer) => { + const { + explicitInput: currentInput, + componentState: { lastSavedInput }, + } = this.getReduxEmbeddableTools().getState(); + getUnsavedChanges + .bind(this)(lastSavedInput, currentInput) + .then((unsavedChanges) => { if (observer.closed) return; - if (getState().componentState.hasUnsavedChanges !== hasChanges) { - dispatch(setHasUnsavedChanges(hasChanges)); - } + + updateUnsavedChangesState.bind(this)(unsavedChanges); + if (useSessionBackup) backupUnsavedChanges.bind(this)(unsavedChanges); }); - }); - }) - ) - .subscribe(); - - setCleanupFunction(() => getHasUnsavedChangesSubscription.unsubscribe()); - }); - - // set initial unsaved changes - checkForUnsavedChanges(initialLastSavedInput, initialInput).then( - async (initialUnsavedChanges) => { - await this.untilInitialized(); - if (!initialUnsavedChanges) return; // early return because we know hasUnsavedChanges has been initialized to false - const { - dispatch, - actions: { setHasUnsavedChanges }, - } = this.getReduxEmbeddableTools(); - dispatch(setHasUnsavedChanges(initialUnsavedChanges)); - } - ); + }); + }) + ) + .subscribe(); + + setCleanupFunction(() => getHasUnsavedChangesSubscription.unsubscribe()); return diffingMiddleware; } +function updateUnsavedChangesState( + this: DashboardContainer, + unsavedChanges: Partial +) { + const { + getState, + dispatch, + actions: { setHasUnsavedChanges }, + } = this.getReduxEmbeddableTools(); + + // dispatch has unsaved changes state + const hasChanges = Object.keys(omit(unsavedChanges, keysNotConsideredUnsavedChanges)).length > 0; + if (getState().componentState.hasUnsavedChanges !== hasChanges) { + dispatch(setHasUnsavedChanges(hasChanges)); + } +} + +function backupUnsavedChanges( + this: DashboardContainer, + unsavedChanges: Partial +) { + const { dashboardSessionStorage } = pluginServices.getServices(); + dashboardSessionStorage.setState( + this.getDashboardSavedObjectId(), + omit(unsavedChanges, keysToOmitFromSessionStorage) + ); +} + /** * Does a shallow diff between @param lastExplicitInput and @param currentExplicitInput and * @returns an object out of the keys which are different. @@ -161,7 +147,8 @@ export async function getUnsavedChanges( lastInput: DashboardContainerByValueInput, input: DashboardContainerByValueInput, keys?: Array -) { +): Promise> { + await this.untilInitialized(); const allKeys = keys ?? ([...new Set([...Object.keys(lastInput), ...Object.keys(input)])] as Array< diff --git a/test/functional/apps/dashboard/group1/dashboard_unsaved_listing.ts b/test/functional/apps/dashboard/group1/dashboard_unsaved_listing.ts index 8c3579070dad..6a6294d1869d 100644 --- a/test/functional/apps/dashboard/group1/dashboard_unsaved_listing.ts +++ b/test/functional/apps/dashboard/group1/dashboard_unsaved_listing.ts @@ -21,8 +21,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const unsavedDashboardTitle = 'New Dashboard'; const newDashboartTitle = 'A Wild Dashboard'; - // Failing: See https://github.com/elastic/kibana/issues/147634 - describe.skip('dashboard unsaved listing', () => { + describe('dashboard unsaved listing', () => { const addSomePanels = async () => { // add an area chart by value await dashboardAddPanel.clickEditorMenuButton(); @@ -151,6 +150,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.dashboard.waitForRenderComplete(); + // wait for the unsaved changes badge to appear. + await PageObjects.dashboard.expectUnsavedChangesBadge(); + // ensure that the unsaved listing exists await PageObjects.dashboard.gotoDashboardLandingPage(); await PageObjects.header.waitUntilLoadingHasFinished(); diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index c7fa5f3d241c..0f5a6d41e6fe 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -324,6 +324,12 @@ export class DashboardPageObject extends FtrService { } } + public async expectUnsavedChangesBadge() { + await this.retry.try(async () => { + await this.testSubjects.existOrFail('dashboardUnsavedChangesBadge'); + }); + } + public async clickNewDashboard(continueEditing = false) { const discardButtonExists = await this.testSubjects.exists('discardDashboardPromptButton'); if (!continueEditing && discardButtonExists) { From 6f3e0a3b952ce26f5e183c3bbda360813cedf3b8 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Tue, 3 Jan 2023 13:01:04 -0500 Subject: [PATCH 18/30] [Dashboards][Portable Dashboard] Fix Performance Metric Baseline (#147798) ## Summary Fixes an accidental difference in dashboard performance tracking baseline --- .../dashboard_container/component/grid/dashboard_grid.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx index 4ef20b6f2d5b..54a15e3c4559 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/grid/dashboard_grid.tsx @@ -111,13 +111,13 @@ interface DashboardPerformanceTracker { doneCount: number; } -const defaultPerformanceTracker: DashboardPerformanceTracker = { +const getDefaultPerformanceTracker: () => DashboardPerformanceTracker = () => ({ panelIds: {}, loadStartTime: performance.now(), lastTimeToData: 0, status: 'done', doneCount: 0, -}; +}); export interface DashboardGridProps { onDataLoaded?: (data: DashboardLoadedInfo) => void; @@ -143,8 +143,8 @@ export const DashboardGrid = ({ onDataLoaded }: DashboardGridProps) => { ); // reset performance tracker on each render. - const performanceRefs = useRef(defaultPerformanceTracker); - performanceRefs.current = defaultPerformanceTracker; + const performanceRefs = useRef(getDefaultPerformanceTracker()); + performanceRefs.current = getDefaultPerformanceTracker(); const onPanelStatusChange = useCallback( (info: EmbeddablePhaseEvent) => { From 46d689220b08ab7db1bb475e1e29be2c17bb76d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 3 Jan 2023 19:28:50 +0100 Subject: [PATCH 19/30] [Logs UI] Refactor log stream query state (#146884) Co-authored-by: Kerry Gallagher Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> closes https://github.com/elastic/kibana/issues/145133 closes https://github.com/elastic/kibana/issues/142764 --- x-pack/plugins/infra/common/runtime_types.ts | 2 +- .../infra/docs/state_machines/README.md | 8 + ...te_usage.md => xstate_machine_patterns.md} | 40 +-- .../state_machines/xstate_react_patterns.md | 137 +++++++++++ .../logs/log_filter/log_filter_state.ts | 149 ----------- .../log_filter/with_log_filter_url_state.tsx | 70 ------ .../logs/log_summary/with_summary.ts | 12 +- .../log_stream_page/state/index.ts | 9 +- .../log_stream_page/state/src/index.ts | 1 + .../state/src/initial_parameters_service.ts | 36 +++ .../log_stream_page/state/src/provider.tsx | 19 +- .../log_stream_page/state/src/selectors.ts | 19 ++ .../state/src/state_machine.ts | 99 +++++++- .../log_stream_page/state/src/types.ts | 18 +- .../log_stream_query_state}/index.ts | 3 +- .../log_stream_query_state/src}/errors.ts | 0 .../log_stream_query_state/src/index.ts | 11 + .../src/notifications.ts | 37 +++ .../src/search_bar_state_service.ts | 61 +++++ .../src/state_machine.ts | 232 ++++++++++++++++++ .../log_stream_query_state/src/types.ts | 96 ++++++++ .../src/url_state_storage_service.ts | 178 ++++++++++++++ .../src/validate_query_service.ts | 70 ++++++ .../xstate_helpers/src/index.ts | 2 + .../src/invalid_state_callout.tsx | 33 +++ .../xstate_helpers/src/types.ts | 43 ++++ .../public/pages/link_to/redirect_to_logs.tsx | 3 +- .../pages/link_to/redirect_to_node_logs.tsx | 4 +- .../pages/logs/shared/page_template.tsx | 2 +- .../components/stream_page_template.tsx | 32 +++ .../infra/public/pages/logs/stream/page.tsx | 37 ++- .../public/pages/logs/stream/page_content.tsx | 105 ++------ .../pages/logs/stream/page_logs_content.tsx | 35 ++- .../stream/page_missing_indices_content.tsx | 13 + .../pages/logs/stream/page_providers.tsx | 73 +++--- .../public/pages/logs/stream/page_toolbar.tsx | 2 +- .../translations/translations/fr-FR.json | 1 - .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 39 files changed, 1263 insertions(+), 431 deletions(-) create mode 100644 x-pack/plugins/infra/docs/state_machines/README.md rename x-pack/plugins/infra/docs/state_machines/{xstate_usage.md => xstate_machine_patterns.md} (86%) create mode 100644 x-pack/plugins/infra/docs/state_machines/xstate_react_patterns.md delete mode 100644 x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts delete mode 100644 x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/initial_parameters_service.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/selectors.ts rename x-pack/plugins/infra/public/{containers/logs/log_filter => observability_logs/log_stream_query_state}/index.ts (75%) rename x-pack/plugins/infra/public/{containers/logs/log_filter => observability_logs/log_stream_query_state/src}/errors.ts (100%) create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/index.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/notifications.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/search_bar_state_service.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/types.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/validate_query_service.ts create mode 100644 x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/invalid_state_callout.tsx create mode 100644 x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/types.ts create mode 100644 x-pack/plugins/infra/public/pages/logs/stream/components/stream_page_template.tsx create mode 100644 x-pack/plugins/infra/public/pages/logs/stream/page_missing_indices_content.tsx diff --git a/x-pack/plugins/infra/common/runtime_types.ts b/x-pack/plugins/infra/common/runtime_types.ts index dc7bb45a6c20..18156e8792d7 100644 --- a/x-pack/plugins/infra/common/runtime_types.ts +++ b/x-pack/plugins/infra/common/runtime_types.ts @@ -36,7 +36,7 @@ const formatError = (error: ValidationError) => error.value )} does not match expected type ${getErrorType(error)}`; -const formatErrors = (errors: ValidationError[]) => +export const formatErrors = (errors: ValidationError[]) => `Failed to validate: \n${errors.map((error) => ` ${formatError(error)}`).join('\n')}`; export const createPlainError = (message: string) => new Error(message); diff --git a/x-pack/plugins/infra/docs/state_machines/README.md b/x-pack/plugins/infra/docs/state_machines/README.md new file mode 100644 index 000000000000..b7eac58a5466 --- /dev/null +++ b/x-pack/plugins/infra/docs/state_machines/README.md @@ -0,0 +1,8 @@ +# `infra` plugin XState state machines patterns + +The Logs UI part of the `infra` plugin uses state machines/state charts based on +the XState library. Please see the following guides for design and +implementation patterns: + +- [Patterns for designing XState state machines](./xstate_machine_patterns.md) +- [Patterns for using XState with React](./xstate_react_patterns.md) diff --git a/x-pack/plugins/infra/docs/state_machines/xstate_usage.md b/x-pack/plugins/infra/docs/state_machines/xstate_machine_patterns.md similarity index 86% rename from x-pack/plugins/infra/docs/state_machines/xstate_usage.md rename to x-pack/plugins/infra/docs/state_machines/xstate_machine_patterns.md index 1f994eca7bf5..adfc0eeb0baf 100644 --- a/x-pack/plugins/infra/docs/state_machines/xstate_usage.md +++ b/x-pack/plugins/infra/docs/state_machines/xstate_machine_patterns.md @@ -1,6 +1,8 @@ +# Patterns for designing XState state machines + ## Summary -Within the Infra plugin (specifically Logs) we use [Xstate](https://xstate.js.org/) for managing state. Xstate brings finite state machines and statecharts to JavaScript and TypeScript. The [Xstate docs](https://xstate.js.org/docs/) themselves are good, but this documentation serves to highlight patterns and certain choices we've made with regards to solution usage. +Within the Infra plugin (specifically Logs) we use [Xstate](https://xstate.js.org/) for managing state. Xstate brings finite state machines and statecharts to JavaScript and TypeScript. The [Xstate docs](https://xstate.js.org/docs/) themselves are good, but this documentation serves to highlight patterns and certain choices we've made with regards to creating and composing state machines in our solution plugin. See [Patterns for using XState with React](./xstate_react_patterns.md) for more patterns specific to UI state machines and their consumption in React component hierarchies. ## Optional actions / exposing events @@ -176,40 +178,6 @@ export const createLogStreamPageStateMachine = ({ Here we call `withConfig()` which returns a new instance with our overrides, in this case we inject the correct services. -## Pairing with React - -There is a [`@xstate/react` library](https://xstate.js.org/docs/recipes/react.html#usage-with-react) that provides some helpful hooks and utilities for combining React and Xstate. - -We have opted to use a provider approach for providing state to the React hierarchy, e.g.: - -```ts -export const useLogStreamPageState = ({ - logViewStateNotifications, -}: { - logViewStateNotifications: LogViewNotificationChannel; -}) => { - const logStreamPageStateService = useInterpret( - () => - createLogStreamPageStateMachine({ - logViewStateNotifications, - }) - ); - - return logStreamPageStateService; -}; - -export const [LogStreamPageStateProvider, useLogStreamPageStateContext] = - createContainer(useLogStreamPageState); -``` - -[`useInterpret`](https://xstate.js.org/docs/packages/xstate-react/#useinterpret-machine-options-observer) returns a **static** reference: - -> returns a static reference (to just the interpreted machine) which will not rerender when its state changes - -When dealing with state it is best to use [selectors](https://xstate.js.org/docs/packages/xstate-react/#useselector-actor-selector-compare-getsnapshot), the `useSelector` hook can significantly increase performance over `useMachine`: - -> This hook will only cause a rerender if the selected value changes, as determined by the optional compare function. - ## TypeScript usage Our usage of Xstate is fully typed. We have opted for a [Typestate](https://xstate.js.org/docs/guides/typescript.html#typestates) approach, which allows us to narrow down the shape of `context` based on the state `value`. [Typegen](https://xstate.js.org/docs/guides/typescript.html#typegen) may be a possible solution in the future, but at the time of writing this causes some friction with the way we work. @@ -220,4 +188,4 @@ We recommend using the [Xstate VSCode extension](https://marketplace.visualstudi When [devTools](https://xstate.js.org/docs/guides/interpretation.html#options) are enabled you can also make use of the [Redux DevTools extension](https://github.com/reduxjs/redux-devtools) to view information about your running state machines. -You can also use [Stately.ai](https://stately.ai/) directly in the browser. \ No newline at end of file +You can also use [Stately.ai](https://stately.ai/) directly in the browser. diff --git a/x-pack/plugins/infra/docs/state_machines/xstate_react_patterns.md b/x-pack/plugins/infra/docs/state_machines/xstate_react_patterns.md new file mode 100644 index 000000000000..829ecc5e88c5 --- /dev/null +++ b/x-pack/plugins/infra/docs/state_machines/xstate_react_patterns.md @@ -0,0 +1,137 @@ +# Patterns for using XState with React + +## Modelling UI state + +The core idea is to have a top-level state machines that corresponds to and "owns" the state of each respective page or section in the UI. Its states are designed to match the decision tree that is performed in the React component hierarchy about which layout is applied and which components should be rendered. + +> **Example:** The page should render an empty state when no log indices can be found, but render the log stream including the search bar when log indices are available. + +That means that branches to render one component or the other should ideally be performed based on the result of the respective `pageState.matches()` call (more specific examples later in the section about connecting components). + +In addition to just representing the UI decision tree, the purpose of the page state machine is also to invoke service state machines, that perform side-effects like data loading or state synchronization with other parts of Kibana. + +> **Example:** The `LogView` service state machine resolves the log view and notifies about its availability via special notification events. + +> **Example:** The `LogStreamQuery` service state machine interacts with Kibana's `querystring`, `filterManager` and URL state facilities and notifies the page state machine about changes. + +## Interpreting and providing UI state machines + +There is a [`@xstate/react` library](https://xstate.js.org/docs/recipes/react.html#usage-with-react) that provides some helpful hooks and utilities for combining React and Xstate. + +We have opted to use a provider approach for providing state to the React hierarchy, e.g.: + +```typescript +export const useLogStreamPageState = ({ + kibanaQuerySettings, + logViewStateNotifications, + queryStringService, + toastsService, + filterManagerService, + urlStateStorage, + useDevTools = isDevMode(), +}: { + useDevTools?: boolean; +} & LogStreamPageStateMachineDependencies) => { + const logStreamPageStateService = useInterpret( + () => + createLogStreamPageStateMachine({ + kibanaQuerySettings, + logViewStateNotifications, + queryStringService, + toastsService, + filterManagerService, + urlStateStorage, + }), + { devTools: useDevTools } + ); + + return logStreamPageStateService; +}; + +export const [LogStreamPageStateProvider, useLogStreamPageStateContext] = + createContainer(useLogStreamPageState); +``` + +[`useInterpret`](https://xstate.js.org/docs/packages/xstate-react/#useinterpret-machine-options-observer) returns a **static** reference: + +> returns a static reference (to just the interpreted machine) which will not rerender when its state changes + +When dealing with state it is best to use [selectors](https://xstate.js.org/docs/packages/xstate-react/#useselector-actor-selector-compare-getsnapshot), the `useSelector` hook can significantly increase performance over `useMachine`: + +> This hook will only cause a rerender if the selected value changes, as determined by the optional compare function. + +## Consuming UI state machines + +So we want to translate the UI layout decision tree that the page state machine represents into a React hierarchy. At the same time we want to keep the individual components on the page independent of the specific page structure so they can be re-used in different contexts. + +> **Example:** The component that renders the log stream is used in the log stream page of the Logs UI, but also in a dashboard embeddable and a shared React component. These "parent components" each have similar, but not identical state structures. + +The redux community has solved that problem using the "connect" pattern, which we should be able to apply with a few small modifications as well. At the core the idea is to leave the presentational components decoupled from a specific page-wide state structure and just have them consume the relevant data and callbacks as props. + +> **Example:** The component that renders the log stream receives log entries and scroll position reporting callbacks as props instead of accessing page-specific state from the context. + +In order to take advantage of the fact that the page layout decisions are already encoded in the page state machine itself, we can use a three-layer component structure: + +- **Presentational components** receive detailed domain-specific data and callbacks as props +- **"For state" components** receive a page state machine state (and if needed a "send"/"dispatch" callback) as props +- **"Connected" components** access the page state machine from context + +This layering preserves several desirable properties: + +- Presentational components can be re-used on different pages and tested in unit-tests and storybooks by just passing the respective props. +- "For state" components are specific to the page state and can match states to branch into different page layouts. They can be tested in unit-tests and storybooks by just passing the corresponding page state data structures. +- "Connected" components know how to gain access to the page state machine from the context and subscribe to their changes using `useActor`. They each work with a specific page state machine provider. They're harder to test, but very slim and of low complexity. + +Assuming the UI state machine is provided as shown in the earlier section, the consumption could look like this: + +**A connected component:** +```typescript +export const ConnectedStreamPageContent: React.FC = () => { + const logStreamPageStateService = useLogStreamPageStateContext(); + + const [logStreamPageState] = useActor(logStreamPageStateService); + + return ; +}; +``` + +**A "for state" layout component:** +```typescript +export const StreamPageContentForState: React.FC<{ logStreamPageState: LogStreamPageState }> = ({ + logStreamPageState, // <-- this could be any state of the page state machine +}) => { + if (logStreamPageState.matches('uninitialized') || logStreamPageState.matches('loadingLogView')) { + return ; + } else if (logStreamPageState.matches('loadingLogViewFailed')) { + return ; + } else if (logStreamPageState.matches('missingLogViewIndices')) { + return ; + } else if (logStreamPageState.matches({ hasLogViewIndices: 'initialized' })) { + return ( // <-- here the matching has narrowed the state to `hasLogViewIndices.initialized` + + + + ); + } else { + return ; + } +}; +``` + +**A "for state" content component** +```typescript +type InitializedLogStreamPageState = MatchedStateFromActor< + LogStreamPageActorRef, + { hasLogViewIndices: 'initialized' } +>; + +export const StreamPageLogsContentForState = React.memo<{ + logStreamPageState: InitializedLogStreamPageState; // <-- this expects to be rendered in a specific state and the compiler will enforce that +}>(({ logStreamPageState }) => { + const { + context: { parsedQuery }, + } = logStreamPageState; + + return ; +}); +``` diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts b/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts deleted file mode 100644 index 4a2f6e280ebe..000000000000 --- a/x-pack/plugins/infra/public/containers/logs/log_filter/log_filter_state.ts +++ /dev/null @@ -1,149 +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 { useMemo, useEffect, useCallback, useState } from 'react'; -import { merge, of } from 'rxjs'; -import { i18n } from '@kbn/i18n'; -import { buildEsQuery, DataViewBase, Query, AggregateQuery, isOfQueryType } from '@kbn/es-query'; -import createContainer from 'constate'; -import { useKibanaQuerySettings } from '../../../utils/use_kibana_query_settings'; -import { BuiltEsQuery } from '../log_stream'; -import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; -import { useSubscription } from '../../../utils/use_observable'; -import { UnsupportedLanguageError, QueryParsingError } from './errors'; - -interface ILogFilterState { - filterQuery: { - parsedQuery: BuiltEsQuery; - serializedQuery: string; - originalQuery: Query; - } | null; - queryStringQuery: Query | AggregateQuery | null; - validationError: Error | null; -} - -export const DEFAULT_QUERY = { - language: 'kuery', - query: '', -}; - -const INITIAL_LOG_FILTER_STATE = { - filterQuery: null, - queryStringQuery: null, - validationError: null, -}; - -// Error toasts -export const errorToastTitle = i18n.translate( - 'xpack.infra.logsPage.toolbar.logFilterErrorToastTitle', - { - defaultMessage: 'Log filter error', - } -); - -const unsupportedLanguageError = i18n.translate( - 'xpack.infra.logsPage.toolbar.logFilterUnsupportedLanguageError', - { - defaultMessage: 'SQL is not supported', - } -); - -export const useLogFilterState = ({ dataView }: { dataView?: DataViewBase }) => { - const { - notifications: { toasts }, - data: { - query: { queryString }, - }, - } = useKibanaContextForPlugin().services; - - const kibanaQuerySettings = useKibanaQuerySettings(); - - const [logFilterState, setLogFilterState] = useState(INITIAL_LOG_FILTER_STATE); - - useEffect(() => { - const handleValidationError = (error: Error) => { - if (error instanceof UnsupportedLanguageError) { - toasts.addError(error, { title: errorToastTitle }); - queryString.setQuery(DEFAULT_QUERY); - } else if (error instanceof QueryParsingError) { - toasts.addError(error, { title: errorToastTitle }); - } - }; - - if (logFilterState.validationError) { - handleValidationError(logFilterState.validationError); - } - }, [logFilterState.validationError, queryString, toasts]); - - const parseQuery = useCallback( - (filterQuery: Query) => { - return buildEsQuery(dataView, filterQuery, [], kibanaQuerySettings); - }, - [dataView, kibanaQuerySettings] - ); - - const getNewLogFilterState = useCallback( - (newQuery: Query | AggregateQuery) => - (previousLogFilterState: ILogFilterState): ILogFilterState => { - try { - if (!isOfQueryType(newQuery)) { - throw new UnsupportedLanguageError(unsupportedLanguageError); - } - try { - const parsedQuery = parseQuery(newQuery); - return { - filterQuery: { - parsedQuery, - serializedQuery: JSON.stringify(parsedQuery), - originalQuery: newQuery, - }, - queryStringQuery: newQuery, - validationError: null, - }; - } catch (error) { - throw new QueryParsingError(error); - } - } catch (error) { - return { - ...previousLogFilterState, - queryStringQuery: newQuery, - validationError: error, - }; - } - }, - [parseQuery] - ); - - useSubscription( - useMemo(() => { - return merge(of(undefined), queryString.getUpdates$()); // NOTE: getUpdates$ uses skip(1) so we do this to ensure an initial emit of a value. - }, [queryString]), - useMemo(() => { - return { - next: () => { - setLogFilterState(getNewLogFilterState(queryString.getQuery())); - }, - }; - }, [getNewLogFilterState, queryString]) - ); - - // NOTE: If the dataView changes the query will need to be reparsed and the filter regenerated. - useEffect(() => { - if (dataView) { - setLogFilterState(getNewLogFilterState(queryString.getQuery())); - } - }, [dataView, getNewLogFilterState, queryString]); - - return { - queryStringQuery: logFilterState.queryStringQuery, // NOTE: Query String Manager query. - filterQuery: logFilterState.filterQuery, // NOTE: Valid and syntactically correct query applied to requests etc. - validationError: logFilterState.validationError, - }; -}; - -export const [LogFilterStateProvider, useLogFilterStateContext] = - createContainer(useLogFilterState); diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx b/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx deleted file mode 100644 index a6f6166b7cf0..000000000000 --- a/x-pack/plugins/infra/public/containers/logs/log_filter/with_log_filter_url_state.tsx +++ /dev/null @@ -1,70 +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 * as rt from 'io-ts'; -import React from 'react'; -import { Query } from '@kbn/es-query'; -import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state'; -import { useLogFilterStateContext, DEFAULT_QUERY } from './log_filter_state'; -import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; - -export const WithLogFilterUrlState: React.FC = () => { - const { - data: { - query: { queryString }, - }, - } = useKibanaContextForPlugin().services; - - const { queryStringQuery } = useLogFilterStateContext(); - - return ( - { - if (urlState) { - queryString.setQuery(urlState); - } - }} - onInitialize={(urlState) => { - if (urlState) { - queryString.setQuery(urlState); - } else { - queryString.setQuery(DEFAULT_QUERY); - } - }} - /> - ); -}; - -const mapToFilterQuery = (value: any): Query | undefined => { - if (legacyFilterQueryUrlStateRT.is(value)) { - // migrate old url state - return { - language: value.kind, - query: value.expression, - }; - } else if (filterQueryUrlStateRT.is(value)) { - return value; - } else { - return undefined; - } -}; - -export const replaceLogFilterInQueryString = (query: Query) => - replaceStateKeyInQueryString('logFilter', query); - -const filterQueryUrlStateRT = rt.type({ - language: rt.string, - query: rt.string, -}); - -const legacyFilterQueryUrlStateRT = rt.type({ - kind: rt.literal('kuery'), - expression: rt.string, -}); diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts index e36ef1caa2c6..102999a19953 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/with_summary.ts @@ -5,10 +5,12 @@ * 2.0. */ +import { useSelector } from '@xstate/react'; +import stringify from 'json-stable-stringify'; import useThrottle from 'react-use/lib/useThrottle'; import { useLogViewContext } from '../../../hooks/use_log_view'; +import { useLogStreamPageStateContext } from '../../../observability_logs/log_stream_page/state'; import { RendererFunction } from '../../../utils/typed_react'; -import { useLogFilterStateContext } from '../log_filter'; import { useLogPositionStateContext } from '../log_position'; import { LogSummaryBuckets, useLogSummary } from './log_summary'; @@ -24,7 +26,11 @@ export const WithSummary = ({ }>; }) => { const { logViewId } = useLogViewContext(); - const { filterQuery } = useLogFilterStateContext(); + const serializedParsedQuery = useSelector(useLogStreamPageStateContext(), (logStreamPageState) => + logStreamPageState.matches({ hasLogViewIndices: 'initialized' }) + ? stringify(logStreamPageState.context.parsedQuery) + : null + ); const { startTimestamp, endTimestamp } = useLogPositionStateContext(); // Keep it reasonably updated for the `now` case, but don't reload all the time when the user scrolls @@ -35,7 +41,7 @@ export const WithSummary = ({ logViewId, throttledStartTimestamp, throttledEndTimestamp, - filterQuery?.serializedQuery ?? null + serializedParsedQuery ); return children({ buckets, start, end }); diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/index.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/index.ts index 86f4bdffa6ed..3b2a320ae181 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/index.ts @@ -5,11 +5,4 @@ * 2.0. */ -export { - createLogStreamPageStateMachine, - LogStreamPageStateProvider, - useLogStreamPageState, - useLogStreamPageStateContext, - type LogStreamPageContext, - type LogStreamPageEvent, -} from './src'; +export * from './src'; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/index.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/index.ts index 88b4fdbbd5d8..22f33aa56174 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/index.ts @@ -6,5 +6,6 @@ */ export * from './provider'; +export * from './selectors'; export * from './state_machine'; export * from './types'; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/initial_parameters_service.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/initial_parameters_service.ts new file mode 100644 index 000000000000..ec1460e41b86 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/initial_parameters_service.ts @@ -0,0 +1,36 @@ +/* + * 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 { InvokeCreator, Receiver } from 'xstate'; +import { ParsedQuery } from '../../../log_stream_query_state'; +import { LogStreamPageContext, LogStreamPageEvent } from './types'; + +export const waitForInitialParameters = + (): InvokeCreator => + (_context, _event) => + (send, onEvent: Receiver) => { + // constituents of the set of initial parameters + let latestValidQuery: ParsedQuery | undefined; + + onEvent((event) => { + switch (event.type) { + // event types that deliver the parameters + case 'VALID_QUERY_CHANGED': + case 'INVALID_QUERY_CHANGED': + latestValidQuery = event.parsedQuery; + break; + } + + // if all constituents of the parameters have been delivered + if (latestValidQuery != null) { + send({ + type: 'RECEIVED_INITIAL_PARAMETERS', + validatedQuery: latestValidQuery, + }); + } + }); + }; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/provider.tsx b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/provider.tsx index 919081143908..b5d31e0ea818 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/provider.tsx +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/provider.tsx @@ -8,20 +8,31 @@ import { useInterpret } from '@xstate/react'; import createContainer from 'constate'; import { isDevMode } from '../../../../utils/dev_mode'; -import { type LogViewNotificationChannel } from '../../../log_view_state'; -import { createLogStreamPageStateMachine } from './state_machine'; +import { + createLogStreamPageStateMachine, + type LogStreamPageStateMachineDependencies, +} from './state_machine'; export const useLogStreamPageState = ({ + kibanaQuerySettings, logViewStateNotifications, + queryStringService, + toastsService, + filterManagerService, + urlStateStorage, useDevTools = isDevMode(), }: { - logViewStateNotifications: LogViewNotificationChannel; useDevTools?: boolean; -}) => { +} & LogStreamPageStateMachineDependencies) => { const logStreamPageStateService = useInterpret( () => createLogStreamPageStateMachine({ + kibanaQuerySettings, logViewStateNotifications, + queryStringService, + toastsService, + filterManagerService, + urlStateStorage, }), { devTools: useDevTools } ); diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/selectors.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/selectors.ts new file mode 100644 index 000000000000..c6bfafd020ab --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/selectors.ts @@ -0,0 +1,19 @@ +/* + * 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 { LogStreamQueryActorRef } from '../../../log_stream_query_state'; +import { MatchedStateFromActor } from '../../../xstate_helpers'; +import { LogStreamPageActorRef } from './state_machine'; + +type LogStreamPageStateWithLogViewIndices = + | MatchedStateFromActor + | MatchedStateFromActor + | MatchedStateFromActor; + +export const selectLogStreamQueryChildService = ( + state: LogStreamPageStateWithLogViewIndices +): LogStreamQueryActorRef => state.children.logStreamQuery; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts index 4b398d61694d..3e343e15bf01 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/state_machine.ts @@ -5,18 +5,25 @@ * 2.0. */ -import { assign, createMachine } from 'xstate'; +import { actions, ActorRefFrom, createMachine, EmittedFrom } from 'xstate'; +import { + createLogStreamQueryStateMachine, + LogStreamQueryStateMachineDependencies, +} from '../../../log_stream_query_state'; import type { LogViewNotificationChannel } from '../../../log_view_state'; +import { OmitDeprecatedState } from '../../../xstate_helpers'; +import { waitForInitialParameters } from './initial_parameters_service'; import type { LogStreamPageContext, LogStreamPageContextWithLogView, LogStreamPageContextWithLogViewError, + LogStreamPageContextWithQuery, LogStreamPageEvent, LogStreamPageTypestate, } from './types'; export const createPureLogStreamPageStateMachine = (initialContext: LogStreamPageContext = {}) => - /** @xstate-layout N4IgpgJg5mDOIC5QBsD2UDKAXATmAhgLYAK+M2+WYAdAK4B2Alk1o-sowF6QDEAMgHkAggBEAkgDkA4gH1BsgGpiAogHUZGACpCASpuUiA2gAYAuolAAHVLEatU9CyAAeiALQAmAJzUPHgGwArIEALAAcHmHhXsEhADQgAJ6IAQDM1IHGYQDs2ZnZxl5e-l6pAL5lCWiYuAQkZGAUVHRMLGwc3BD8wuLScgKKKuoAYkJifAYm5kgg1rb2jjOuCJ4hAIzUqZklHgX+xqlrkQnJCKlB1P4loYH+qV7hHoEVVejYeESk5FiUNAzMdnaXF4glEklk8hkSjUGgAqgBheHKAyTMxOOaAhxOZbZNbZajGXLBVIkrJrYInRBHYzUEIeVIhVJhNZZLws7IhF4garvOpfRo-Zr-NrsYFdUG9CEDKFDOGI5EiSZraZWGyYxagHEhEIEwkeI7Zc7GO6UhCZHzZML7MKBDwhQp0zmVblvWqfBpNGhofAQZhQPjoBSMMAAd26YL6kOhIzGEyMaJmGIW2JS4VpJTCWXp5K82Q8pvN1Et1tt9oedq5PLd9W+v2o3t99H9geDYYl4P6gxhGARSJR8ZVszVyaWiEZPnt-m8Ry8xjta38pqN1FK+uy+w8xhCgS8lddHxrArrDb9AagQdD4clnZl3d7CqVg6TjCxo4QISumy2MWNMWiAVNO58WMFkImMOc50CMI9xqA9+U9etUB9U8W1DYZ8EYZAQR6Dso1lLRdH0Ad0WHF8NRcdxvF8AJYgiKIwhiUIC1SDwMgNcC8g5O1nmdKs4I9QUaAAC3wWAzwvEMxHoX0AGM4BaAFWFFToeB0ZQkTEBQDBkIQ+D4GRiF0IQAFllH0HQMCmEj5jIlMEDWFj0mNfw1i8SJcVCVJTXtfEGJc7d6RCLjDQqZ16FQCA4CcPi+QE35rPVOzPAYzZtjcvYDgc003DWHVCSCIJbkNWcvCtGDeXdWshVaQFlMgBKR01Md8ySKk6WoclwKyG02SCLdyureDBMQ5Cm3E1sGtst9dgyUIritG1ch-eJWrOMIwk2Yojn8PMtj8HjXlg2Kqq9JDG2bc9W3QzD6sTUjXyas1qGZO03LuHa2R6wCdyLVIORAlk6VKgb+JO6gRLE1DJOkxg5PgO6bIeij7IcnVHlufwQlnFzMaXTdNpcqC8jZQ5chB46j2aCHxtDKTZPk4Vao6W7VUR8jljWNYIlpCIMax40FxW04fOeraAoZYLyl4-cKYQ6mobp2H5MUoFOkmpGOZ3fEdvWQ0STc21vMJUX-NtCW6Wg6WjsqymaEIRhYFsMaFZhuH1fZqlDn8XxCj8RisiK76LT+v7cTDg4pYqIA */ + /** @xstate-layout N4IgpgJg5mDOIC5QBsD2UDKAXATmAhgLYAK+M2+WYAdAK4B2Alk1o-sowF6QDEAMgHkAggBEAkgDkA4gH1BsgGpiAogHUZGACpCASpuUiA2gAYAuolAAHVLEatU9CyAAeiALQAmAJzUPHgGwArIEALAAcHmHhXsEhADQgAJ6IAQDM1IHGYQDs2ZnZxl5e-l6pAL5lCWiYuAQkZGAUVHRMLGwc3BD8wuLScgKKKuoAYkJifAYm5kgg1rb2jjOuCJ4hAIzUqZklHgX+xqlrkQnJCKlB1P4loYH+qV7hHoEVVejYeESk5FiUNAzMdnaXF4glEklk8hkSjUGgAqgBheHKAyTMxOOaAhxOZbZNbZajGXLBVIkrJrYInRBHYzUEIeVIhVJhNZZLws7IhF4garvOpfRo-Zr-NrsYFdUG9CEDKFDOGI5EiSZraZWGyYxagHEhEIEwkeI7Zc7GO6UhCZHzZML7MKBDwhQp0zmVblvWqfBpNGhofAQZhQPjoBSMMAAd26YL6kOhIzGEyMaJmGIW2JS4VpJTCWXp5K82Q8pvN1Et1tt9oedq5PLd9W+v2o3t99H9geDYYl4P6gxhGARSJR8ZVszVyaWiEZPnt-m8Ry8xjta38pqN1FK+uy+w8xhCgS8lddHxrArrDb9AagQdD4clnZl3d7CqVg6TjCxo4QISumy2MWNMWiAVNO58WMFkImMOc50CMI9xqA9+U9etUB9U8W1DYZ8EYZAQR6Dso1lLRdH0Ad0WHF8NRcdxvF8AJYgiKIwhiUIC1SDwMgNcC8g5O1nmdKs4I9QUaAAC3wWAzwvEMxHoX0AGM4BaAFWFFToeB0ZQkTEBQDBkSQxE0MQhD4GRiF0IQAFllH0HQMCmEj5jIlMEBnfxqAiYojjWVJCjnJcwnSIIrSuNZZ2CGJyl4-c+QEusRLE1DJOkxg5NgBSRQ6XgFEMsQRBkABFWFlB0ABNGR4QACSEaRUSfUjX01Kl-DyWk1giW0p3tElTTxfFwhCPYQIXXE1idV5YKi2tmli8TWyk2T5OFQFlN4SRMr4bK8oK4rSoqqriMTWryOWBdGtckCQMCEknn8MIl21FcWLxVJDUzfwWv8GDeXdCbhNE6bQ1mpL5MUoEVNW9b8sKkrysqqRqrs9VHMGwJmtagI7QOVICznFd1yebdMgutY1gqZ16FQCA4CcPjxqPKh4ZHeqVkiHwtl-XZjQOTzTTcNk2NtQ4-A5q0PureDBNSxb0ogemHLfOkurpahyXArIbTZIItxF-jvsQ5Cmz+kMZbqiiEF2DJQiuK0bVyH94iSRAmTCTZ3ICPMtj8HjRs+w8EJPfX4vQzDICNw7EGR5k7S8NJGrZNXAJ3IsnvtImt28aCIrGr7aZ+uLzxmxLkpDxHNxpC7dn2K404pe331YrwokNQ1GIF7ItZphCpvigHkolpSpaLt8jjTMv12NKd6+r04nlYgbDjxO0-KnNus4736u4LoG0rFAfGc8qIi0Cck1cCQ1K4LJ4iznS1zjzG0WOXn3xcIRhYFsf28-+jf4H2+zjaOw4XKbijt4YIWRbjZHjhaJ6T1cSwIxiTMoQA */ createMachine( { context: initialContext, @@ -77,15 +84,47 @@ export const createPureLogStreamPageStateMachine = (initialContext: LogStreamPag }, hasLogViewIndices: { initial: 'uninitialized', + states: { uninitialized: { + invoke: { + src: 'waitForInitialParameters', + id: 'waitForInitialParameters', + }, + on: { - RECEIVED_ALL_PARAMETERS: { + RECEIVED_INITIAL_PARAMETERS: { target: 'initialized', + actions: 'storeQuery', + }, + + VALID_QUERY_CHANGED: { + target: 'uninitialized', + internal: true, + actions: 'forwardToInitialParameters', + }, + + INVALID_QUERY_CHANGED: { + target: 'uninitialized', + internal: true, + actions: 'forwardToInitialParameters', + }, + }, + }, + initialized: { + on: { + VALID_QUERY_CHANGED: { + target: 'initialized', + internal: true, + actions: 'storeQuery', }, }, }, - initialized: {}, + }, + + invoke: { + src: 'logStreamQuery', + id: 'logStreamQuery', }, }, missingLogViewIndices: {}, @@ -93,12 +132,13 @@ export const createPureLogStreamPageStateMachine = (initialContext: LogStreamPag }, { actions: { - storeLogViewError: assign((_context, event) => + forwardToInitialParameters: actions.forwardTo('waitForInitialParameters'), + storeLogViewError: actions.assign((_context, event) => event.type === 'LOADING_LOG_VIEW_FAILED' ? ({ logViewError: event.error } as LogStreamPageContextWithLogViewError) : {} ), - storeResolvedLogView: assign((_context, event) => + storeResolvedLogView: actions.assign((_context, event) => event.type === 'LOADING_LOG_VIEW_SUCCEEDED' ? ({ logViewStatus: event.status, @@ -106,6 +146,17 @@ export const createPureLogStreamPageStateMachine = (initialContext: LogStreamPag } as LogStreamPageContextWithLogView) : {} ), + storeQuery: actions.assign((_context, event) => + event.type === 'RECEIVED_INITIAL_PARAMETERS' + ? ({ + parsedQuery: event.validatedQuery, + } as LogStreamPageContextWithQuery) + : event.type === 'VALID_QUERY_CHANGED' + ? ({ + parsedQuery: event.parsedQuery, + } as LogStreamPageContextWithQuery) + : {} + ), }, guards: { hasLogViewIndices: (_context, event) => @@ -115,13 +166,43 @@ export const createPureLogStreamPageStateMachine = (initialContext: LogStreamPag } ); +export type LogStreamPageStateMachine = ReturnType; +export type LogStreamPageActorRef = OmitDeprecatedState>; +export type LogStreamPageState = EmittedFrom; + +export type LogStreamPageStateMachineDependencies = { + logViewStateNotifications: LogViewNotificationChannel; +} & LogStreamQueryStateMachineDependencies; + export const createLogStreamPageStateMachine = ({ + kibanaQuerySettings, logViewStateNotifications, -}: { - logViewStateNotifications: LogViewNotificationChannel; -}) => + queryStringService, + toastsService, + filterManagerService, + urlStateStorage, +}: LogStreamPageStateMachineDependencies) => createPureLogStreamPageStateMachine().withConfig({ services: { logViewNotifications: () => logViewStateNotifications.createService(), + logStreamQuery: (context) => { + if (!('resolvedLogView' in context)) { + throw new Error('Failed to spawn log stream query service: no LogView in context'); + } + + return createLogStreamQueryStateMachine( + { + dataViews: [context.resolvedLogView.dataViewReference], + }, + { + kibanaQuerySettings, + queryStringService, + toastsService, + filterManagerService, + urlStateStorage, + } + ); + }, + waitForInitialParameters: waitForInitialParameters(), }, }); diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts index e1cdeffcdc4c..7a970a3e06e7 100644 --- a/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_page/state/src/types.ts @@ -6,6 +6,8 @@ */ import type { LogViewStatus } from '../../../../../common/log_views'; +import { ParsedQuery } from '../../../log_stream_query_state'; +import { LogStreamQueryNotificationEvent } from '../../../log_stream_query_state/src/notifications'; import type { LogViewContextWithError, LogViewContextWithResolvedLogView, @@ -14,8 +16,10 @@ import type { export type LogStreamPageEvent = | LogViewNotificationEvent + | LogStreamQueryNotificationEvent | { - type: 'RECEIVED_ALL_PARAMETERS'; + type: 'RECEIVED_INITIAL_PARAMETERS'; + validatedQuery: ParsedQuery; }; export interface LogStreamPageContextWithLogView { @@ -27,6 +31,10 @@ export interface LogStreamPageContextWithLogViewError { logViewError: LogViewContextWithError['error']; } +export interface LogStreamPageContextWithQuery { + parsedQuery: ParsedQuery; +} + export type LogStreamPageTypestate = | { value: 'uninitialized'; @@ -44,6 +52,14 @@ export type LogStreamPageTypestate = value: 'hasLogViewIndices'; context: LogStreamPageContextWithLogView; } + | { + value: { hasLogViewIndices: 'uninitialized' }; + context: LogStreamPageContextWithLogView; + } + | { + value: { hasLogViewIndices: 'initialized' }; + context: LogStreamPageContextWithLogView & LogStreamPageContextWithQuery; + } | { value: 'missingLogViewIndices'; context: LogStreamPageContextWithLogView; diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/index.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/index.ts similarity index 75% rename from x-pack/plugins/infra/public/containers/logs/log_filter/index.ts rename to x-pack/plugins/infra/public/observability_logs/log_stream_query_state/index.ts index 5e8bafe5603c..3b2a320ae181 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_filter/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/index.ts @@ -5,5 +5,4 @@ * 2.0. */ -export * from './log_filter_state'; -export * from './with_log_filter_url_state'; +export * from './src'; diff --git a/x-pack/plugins/infra/public/containers/logs/log_filter/errors.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/errors.ts similarity index 100% rename from x-pack/plugins/infra/public/containers/logs/log_filter/errors.ts rename to x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/errors.ts diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/index.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/index.ts new file mode 100644 index 000000000000..5d89520ed2ac --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/index.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +export * from './errors'; +export * from './state_machine'; +export * from './types'; +export * from './url_state_storage_service'; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/notifications.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/notifications.ts new file mode 100644 index 000000000000..3599144d9fd4 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/notifications.ts @@ -0,0 +1,37 @@ +/* + * 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 { LogStreamQueryContext, ParsedQuery } from './types'; + +export type LogStreamQueryNotificationEvent = + | { + type: 'VALID_QUERY_CHANGED'; + parsedQuery: ParsedQuery; + } + | { + type: 'INVALID_QUERY_CHANGED'; + parsedQuery: ParsedQuery; + error: Error; + }; + +export const logStreamQueryNotificationEventSelectors = { + validQueryChanged: (context: LogStreamQueryContext) => + 'parsedQuery' in context + ? ({ + type: 'VALID_QUERY_CHANGED', + parsedQuery: context.parsedQuery, + } as LogStreamQueryNotificationEvent) + : undefined, + invalidQueryChanged: (context: LogStreamQueryContext) => + 'validationError' in context + ? ({ + type: 'INVALID_QUERY_CHANGED', + parsedQuery: context.parsedQuery, + error: context.validationError, + } as LogStreamQueryNotificationEvent) + : undefined, +}; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/search_bar_state_service.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/search_bar_state_service.ts new file mode 100644 index 000000000000..85a216dba247 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/search_bar_state_service.ts @@ -0,0 +1,61 @@ +/* + * 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 { FilterManager, QueryStringContract } from '@kbn/data-plugin/public'; +import { map } from 'rxjs/operators'; +import type { InvokeCreator } from 'xstate'; +import type { LogStreamQueryContext, LogStreamQueryEvent } from './types'; + +export const subscribeToQuerySearchBarChanges = + ({ + queryStringService, + }: { + queryStringService: QueryStringContract; + }): InvokeCreator => + (context) => + queryStringService.getUpdates$().pipe( + map(() => queryStringService.getQuery()), + map((query): LogStreamQueryEvent => { + return { + type: 'QUERY_FROM_SEARCH_BAR_CHANGED', + query, + }; + }) + ); + +export const updateQueryInSearchBar = + ({ queryStringService }: { queryStringService: QueryStringContract }) => + (context: LogStreamQueryContext, event: LogStreamQueryEvent) => { + if ('query' in context) { + queryStringService.setQuery(context.query); + } + }; + +export const subscribeToFilterSearchBarChanges = + ({ + filterManagerService, + }: { + filterManagerService: FilterManager; + }): InvokeCreator => + (context) => + filterManagerService.getUpdates$().pipe( + map(() => filterManagerService.getFilters()), + map((filters): LogStreamQueryEvent => { + return { + type: 'FILTERS_FROM_SEARCH_BAR_CHANGED', + filters, + }; + }) + ); + +export const updateFiltersInSearchBar = + ({ filterManagerService }: { filterManagerService: FilterManager }) => + (context: LogStreamQueryContext, event: LogStreamQueryEvent) => { + if ('filters' in context) { + filterManagerService.setFilters(context.filters); + } + }; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts new file mode 100644 index 000000000000..951551534d74 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/state_machine.ts @@ -0,0 +1,232 @@ +/* + * 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 { IToasts } from '@kbn/core-notifications-browser'; +import type { FilterManager, QueryStringContract } from '@kbn/data-plugin/public'; +import { EsQueryConfig } from '@kbn/es-query'; +import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; +import { actions, ActorRefFrom, createMachine, SpecialTargets } from 'xstate'; +import { OmitDeprecatedState, sendIfDefined } from '../../xstate_helpers'; +import { logStreamQueryNotificationEventSelectors } from './notifications'; +import { + subscribeToFilterSearchBarChanges, + subscribeToQuerySearchBarChanges, + updateFiltersInSearchBar, + updateQueryInSearchBar, +} from './search_bar_state_service'; +import type { + LogStreamQueryContext, + LogStreamQueryContextWithDataViews, + LogStreamQueryContextWithFilters, + LogStreamQueryContextWithParsedQuery, + LogStreamQueryContextWithQuery, + LogStreamQueryContextWithValidationError, + LogStreamQueryEvent, + LogStreamQueryTypestate, +} from './types'; +import { + initializeFromUrl, + safeDefaultParsedQuery, + updateFiltersInUrl, + updateQueryInUrl, +} from './url_state_storage_service'; +import { showValidationErrorToast, validateQuery } from './validate_query_service'; + +export const createPureLogStreamQueryStateMachine = ( + initialContext: LogStreamQueryContextWithDataViews +) => + /** @xstate-layout N4IgpgJg5mDOIC5QEUCuYBOBPAdKgdgJZEAuhAhgDaEBekAxANoAMAuoqAA4D2shZ3fBxAAPRAFoATAHZJOAMwBWJQBYAjNIBszAJwAOTZIA0ILBMnyca+Wp2bFenfL1b5zTQF8PJtJlzF+CmoaYigAMQxuAFsAVQxKegBJADlEgBVEgEEAGUSALQBRABEAfTCAJQB5AFkSmPLslnYkEB4+ASEWsQRxeRU5PXdNA001RX1tRRMzBBkdHHVmFWZpHTVJC2ZFFS8fdGwcAAtyWF9semQYgvKATTKq2oBlAszygGEACRKAIVeSz8yyQA4sUmsI2oFBMJupIVNIcLoVHpBoppLJFMZTIh7DgdMsnNINHZlip5LsQGdcMdTvssPQwolsmlro97jUSs9Xp8fn8AcDQWxwbxIZ1QN1tHICWoXJJNDoMfJ5NNsYpcfj5ITVpoSZ5vBTaUcTpT6EVMmlMiUAGqJAoAdVZfJBRTBLQhHWhiDscllKwsKnUOmYajUyoQqysmmkKh00mYbijjkU5MphppfhwGDAADcqIQIOQyPgoPRLTlEqaMpVkmVMoyBc0uML3V1PZHcfK1P6UXY0aG3JZ5Pp1WpRgZJEm9SnqSnMznqPnC8XS7kK4kqxyYm83gVivWhe1CFCWwhB8wFjJRvJJINZZHpH24woh7obKPDBO9um53mC6ES2XV3XR5N23XdnUFV0m0PUVRAkNZcVkUY8UUQxmDjPRQ39NQcCjNENUkWwVEUJZpGTA1vwXP9l3LM012rMJa2yPdIIPI8xQkaRLB0CZiO2aQ9EkJxQxQ1UBKUZg9HWUl1FI8l8G4CA4GESl9xFD0elPHBBk0YYdLGCYtlDKRtARcYtivFw0S0Mj0wIAIyFzOgIFU5t2J6QS9BwDYVmlHRYR0rZNCMjRsJHST-RE8dOJ2ScDXsoJaFCCJojiSgXOg9Ten6LzI1GawrKvWFQ07SxZE41Y1jsawP31dNp1pdK2NghBbHmRFkS2NFx0xGZxA0ORFDMyMPNWcYbIOeqv1zZyWLU48NWwyqtTcXQ9FJTDlgQ-pkW1HbJPGqkjTi-AKMamDuj8lQEWlBwVAlWUrw2s8Y22gwkQMfbYrqo701nabfyLM71NjbCBOWCTfLhdxQ1hM9ZScPQNW4xQR2sA6cAogGoCB49eiULTL1RPQMUDQcpixBB-WeqNx2lIlEdkrwgA */ + createMachine( + { + context: initialContext, + preserveActionOrder: true, + predictableActionArguments: true, + id: 'Query', + initial: 'uninitialized', + states: { + uninitialized: { + always: { + target: 'initializingFromUrl', + }, + }, + + initializingFromUrl: { + on: { + INITIALIZED_FROM_URL: { + target: 'validating', + actions: ['storeQuery', 'storeFilters'], + }, + }, + + invoke: { + src: 'initializeFromUrl', + }, + }, + + hasQuery: { + entry: ['updateQueryInUrl', 'updateQueryInSearchBar', 'updateFiltersInSearchBar'], + invoke: [ + { + src: 'subscribeToQuerySearchBarChanges', + }, + { + src: 'subscribeToFilterSearchBarChanges', + }, + ], + initial: 'revalidating', + states: { + valid: { + entry: 'notifyValidQueryChanged', + }, + invalid: { + entry: 'notifyInvalidQueryChanged', + }, + revalidating: { + invoke: { + src: 'validateQuery', + }, + on: { + VALIDATION_FAILED: { + target: 'invalid', + actions: ['storeValidationError', 'showValidationErrorToast'], + }, + VALIDATION_SUCCEEDED: { + target: 'valid', + actions: ['clearValidationError', 'storeParsedQuery'], + }, + }, + }, + }, + on: { + QUERY_FROM_SEARCH_BAR_CHANGED: { + target: '.revalidating', + actions: ['storeQuery', 'updateQueryInUrl'], + }, + + FILTERS_FROM_SEARCH_BAR_CHANGED: { + target: '.revalidating', + actions: ['storeFilters', 'updateFiltersInUrl'], + }, + + DATA_VIEWS_CHANGED: { + target: '.revalidating', + actions: 'storeDataViews', + }, + }, + }, + + validating: { + invoke: { + src: 'validateQuery', + }, + + on: { + VALIDATION_SUCCEEDED: { + target: 'hasQuery.valid', + actions: 'storeParsedQuery', + }, + + VALIDATION_FAILED: { + target: 'hasQuery.invalid', + actions: [ + 'storeValidationError', + 'storeDefaultParsedQuery', + 'showValidationErrorToast', + ], + }, + }, + }, + }, + }, + { + actions: { + notifyInvalidQueryChanged: actions.pure(() => undefined), + notifyValidQueryChanged: actions.pure(() => undefined), + storeQuery: actions.assign((_context, event) => { + return 'query' in event ? ({ query: event.query } as LogStreamQueryContextWithQuery) : {}; + }), + storeFilters: actions.assign((_context, event) => + 'filters' in event ? ({ filters: event.filters } as LogStreamQueryContextWithFilters) : {} + ), + storeDataViews: actions.assign((_context, event) => + 'dataViews' in event + ? ({ dataViews: event.dataViews } as LogStreamQueryContextWithDataViews) + : {} + ), + storeValidationError: actions.assign((_context, event) => + 'error' in event + ? ({ + validationError: event.error, + } as LogStreamQueryContextWithQuery & LogStreamQueryContextWithValidationError) + : {} + ), + storeDefaultParsedQuery: actions.assign( + (_context, _event) => + ({ parsedQuery: safeDefaultParsedQuery } as LogStreamQueryContextWithParsedQuery) + ), + storeParsedQuery: actions.assign((_context, event) => + 'parsedQuery' in event + ? ({ parsedQuery: event.parsedQuery } as LogStreamQueryContextWithParsedQuery) + : {} + ), + clearValidationError: actions.assign( + (_context, _event) => + ({ validationError: undefined } as Omit< + LogStreamQueryContextWithValidationError, + 'validationError' + >) + ), + }, + } + ); + +export interface LogStreamQueryStateMachineDependencies { + kibanaQuerySettings: EsQueryConfig; + queryStringService: QueryStringContract; + filterManagerService: FilterManager; + urlStateStorage: IKbnUrlStateStorage; + toastsService: IToasts; +} + +export const createLogStreamQueryStateMachine = ( + initialContext: LogStreamQueryContextWithDataViews, + { + kibanaQuerySettings, + queryStringService, + toastsService, + filterManagerService, + urlStateStorage, + }: LogStreamQueryStateMachineDependencies +) => + createPureLogStreamQueryStateMachine(initialContext).withConfig({ + actions: { + notifyInvalidQueryChanged: sendIfDefined(SpecialTargets.Parent)( + logStreamQueryNotificationEventSelectors.invalidQueryChanged + ), + notifyValidQueryChanged: sendIfDefined(SpecialTargets.Parent)( + logStreamQueryNotificationEventSelectors.validQueryChanged + ), + showValidationErrorToast: showValidationErrorToast({ toastsService }), + updateQueryInUrl: updateQueryInUrl({ toastsService, urlStateStorage }), + updateQueryInSearchBar: updateQueryInSearchBar({ queryStringService }), + updateFiltersInUrl: updateFiltersInUrl({ toastsService, urlStateStorage }), + updateFiltersInSearchBar: updateFiltersInSearchBar({ filterManagerService }), + }, + services: { + initializeFromUrl: initializeFromUrl({ toastsService, urlStateStorage }), + validateQuery: validateQuery({ kibanaQuerySettings }), + subscribeToQuerySearchBarChanges: subscribeToQuerySearchBarChanges({ + queryStringService, + }), + subscribeToFilterSearchBarChanges: subscribeToFilterSearchBarChanges({ + filterManagerService, + }), + }, + }); + +export type LogStreamQueryStateMachine = ReturnType; +export type LogStreamQueryActorRef = OmitDeprecatedState>; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/types.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/types.ts new file mode 100644 index 000000000000..46706254f446 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/types.ts @@ -0,0 +1,96 @@ +/* + * 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 { AggregateQuery, BoolQuery, DataViewBase, Query, Filter } from '@kbn/es-query'; + +export type AnyQuery = Query | AggregateQuery; + +export interface ParsedQuery { + bool: BoolQuery; +} + +export interface LogStreamQueryContextWithDataViews { + dataViews: DataViewBase[]; +} + +export interface LogStreamQueryContextWithSavedQueryId { + savedQueryId: string; +} +export interface LogStreamQueryContextWithQuery { + query: AnyQuery; +} + +export interface LogStreamQueryContextWithParsedQuery { + parsedQuery: ParsedQuery; +} + +export interface LogStreamQueryContextWithFilters { + filters: Filter[]; +} + +export interface LogStreamQueryContextWithValidationError { + validationError: Error; +} + +export type LogStreamQueryTypestate = + | { + value: 'uninitialized'; + context: LogStreamQueryContextWithDataViews; + } + | { + value: 'hasQuery' | { hasQuery: 'validating' }; + context: LogStreamQueryContextWithDataViews & + LogStreamQueryContextWithParsedQuery & + LogStreamQueryContextWithQuery & + LogStreamQueryContextWithFilters; + } + | { + value: { hasQuery: 'valid' }; + context: LogStreamQueryContextWithDataViews & + LogStreamQueryContextWithParsedQuery & + LogStreamQueryContextWithQuery & + LogStreamQueryContextWithFilters; + } + | { + value: { hasQuery: 'invalid' }; + context: LogStreamQueryContextWithDataViews & + LogStreamQueryContextWithParsedQuery & + LogStreamQueryContextWithQuery & + LogStreamQueryContextWithFilters & + LogStreamQueryContextWithValidationError; + }; + +export type LogStreamQueryContext = LogStreamQueryTypestate['context']; + +export type LogStreamQueryStateValue = LogStreamQueryTypestate['value']; + +export type LogStreamQueryEvent = + | { + type: 'QUERY_FROM_SEARCH_BAR_CHANGED'; + query: AnyQuery; + } + | { + type: 'FILTERS_FROM_SEARCH_BAR_CHANGED'; + filters: Filter[]; + } + | { + type: 'DATA_VIEWS_CHANGED'; + dataViews: DataViewBase[]; + } + | { + type: 'VALIDATION_SUCCEEDED'; + parsedQuery: ParsedQuery; + } + | { + type: 'VALIDATION_FAILED'; + error: Error; + } + | { + type: 'INITIALIZED_FROM_URL'; + query: AnyQuery; + filters: Filter[]; + }; diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts new file mode 100644 index 000000000000..c18ee71ed4f8 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/url_state_storage_service.ts @@ -0,0 +1,178 @@ +/* + * 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 { IToasts } from '@kbn/core-notifications-browser'; +import { Query } from '@kbn/es-query'; +import { IKbnUrlStateStorage, withNotifyOnErrors } from '@kbn/kibana-utils-plugin/public'; +import * as Array from 'fp-ts/lib/Array'; +import * as Either from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/function'; +import * as rt from 'io-ts'; +import { InvokeCreator } from 'xstate'; +import { createPlainError, formatErrors } from '../../../../common/runtime_types'; +import { replaceStateKeyInQueryString } from '../../../utils/url_state'; +import type { LogStreamQueryContext, LogStreamQueryEvent, ParsedQuery } from './types'; + +interface LogStreamQueryUrlStateDependencies { + filterStateKey?: string; + savedQueryIdKey?: string; + toastsService: IToasts; + urlStateStorage: IKbnUrlStateStorage; +} + +const defaultFilterStateKey = 'logFilter'; +const defaultFilterStateValue: Required = { + query: { + language: 'kuery', + query: '', + }, + filters: [], +}; +export const safeDefaultParsedQuery: ParsedQuery = { + bool: { + must: [], + must_not: [], + should: [], + filter: [{ match_none: {} }], + }, +}; + +export const updateQueryInUrl = + ({ + urlStateStorage, + filterStateKey = defaultFilterStateKey, + }: LogStreamQueryUrlStateDependencies) => + (context: LogStreamQueryContext, _event: LogStreamQueryEvent) => { + if (!('query' in context)) { + throw new Error(); + } + + urlStateStorage.set( + filterStateKey, + filterStateInUrlRT.encode({ + query: context.query, + filters: context.filters, + }) + ); + }; + +export const updateFiltersInUrl = + ({ + urlStateStorage, + filterStateKey = defaultFilterStateKey, + }: LogStreamQueryUrlStateDependencies) => + (context: LogStreamQueryContext, _event: LogStreamQueryEvent) => { + if (!('filters' in context)) { + throw new Error(); + } + + urlStateStorage.set( + filterStateKey, + filterStateInUrlRT.encode({ + query: context.query, + filters: context.filters, + }) + ); + }; + +export const initializeFromUrl = + ({ + filterStateKey = defaultFilterStateKey, + toastsService, + urlStateStorage, + }: LogStreamQueryUrlStateDependencies): InvokeCreator< + LogStreamQueryContext, + LogStreamQueryEvent + > => + (_context, _event) => + (send) => { + const queryValueFromUrl = urlStateStorage.get(filterStateKey) ?? defaultFilterStateValue; + + const queryE = decodeQueryValueFromUrl(queryValueFromUrl); + + if (Either.isLeft(queryE)) { + withNotifyOnErrors(toastsService).onGetError(createPlainError(formatErrors(queryE.left))); + + send({ + type: 'INITIALIZED_FROM_URL', + query: defaultFilterStateValue.query, + filters: defaultFilterStateValue.filters, + }); + } else { + send({ + type: 'INITIALIZED_FROM_URL', + query: queryE.right.query ?? defaultFilterStateValue.query, + filters: queryE.right.filters ?? defaultFilterStateValue.filters, + }); + } + }; + +const filterMeta = rt.partial({ + alias: rt.union([rt.string, rt.null]), + disabled: rt.boolean, + negate: rt.boolean, + controlledBy: rt.string, + group: rt.string, + index: rt.string, + isMultiIndex: rt.boolean, + type: rt.string, + key: rt.string, + params: rt.any, + value: rt.any, +}); + +const filter = rt.intersection([ + rt.type({ + meta: filterMeta, + }), + rt.partial({ + query: rt.UnknownRecord, + }), +]); + +const filterStateInUrlRT = rt.partial({ + query: rt.union([ + rt.strict({ + language: rt.string, + query: rt.union([rt.string, rt.record(rt.string, rt.unknown)]), + }), + rt.strict({ + sql: rt.string, + }), + rt.strict({ + esql: rt.string, + }), + ]), + filters: rt.array(filter), +}); + +type FilterStateInUrl = rt.TypeOf; + +const legacyFilterStateInUrlRT = rt.union([ + rt.strict({ + language: rt.string, + query: rt.union([rt.string, rt.record(rt.string, rt.unknown)]), + }), + rt.strict({ + sql: rt.string, + }), + rt.strict({ + esql: rt.string, + }), +]); + +const decodeQueryValueFromUrl = (queryValueFromUrl: unknown) => + Either.getAltValidation(Array.getMonoid()).alt( + pipe( + legacyFilterStateInUrlRT.decode(queryValueFromUrl), + Either.map((legacyQuery) => ({ query: legacyQuery })) + ), + () => filterStateInUrlRT.decode(queryValueFromUrl) + ); + +export const replaceLogFilterInQueryString = (query: Query) => + replaceStateKeyInQueryString(defaultFilterStateKey, query); diff --git a/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/validate_query_service.ts b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/validate_query_service.ts new file mode 100644 index 000000000000..05227ad2234f --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/log_stream_query_state/src/validate_query_service.ts @@ -0,0 +1,70 @@ +/* + * 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 { IToasts } from '@kbn/core-notifications-browser'; +import { buildEsQuery, EsQueryConfig, isOfQueryType } from '@kbn/es-query'; +import { i18n } from '@kbn/i18n'; +import type { InvokeCreator } from 'xstate'; +import { QueryParsingError, UnsupportedLanguageError } from './errors'; +import type { LogStreamQueryContext, LogStreamQueryEvent } from './types'; + +export const validateQuery = + ({ + kibanaQuerySettings, + }: { + kibanaQuerySettings: EsQueryConfig; + }): InvokeCreator => + (context) => + (send) => { + if (!('query' in context)) { + throw new Error('Failed to validate query: no query in context'); + } + + const { dataViews, query, filters } = context; + + if (!isOfQueryType(query)) { + send({ + type: 'VALIDATION_FAILED', + error: new UnsupportedLanguageError('Failed to validate query: unsupported language'), + }); + + return; + } + + try { + const parsedQuery = buildEsQuery(dataViews, query, filters, kibanaQuerySettings); + + send({ + type: 'VALIDATION_SUCCEEDED', + parsedQuery, + }); + } catch (error) { + send({ + type: 'VALIDATION_FAILED', + error: new QueryParsingError(`${error}`), + }); + } + }; + +export const showValidationErrorToast = + ({ toastsService }: { toastsService: IToasts }) => + (_context: LogStreamQueryContext, event: LogStreamQueryEvent) => { + if (event.type !== 'VALIDATION_FAILED') { + return; + } + + toastsService.addError(event.error, { + title: validationErrorToastTitle, + }); + }; + +const validationErrorToastTitle = i18n.translate( + 'xpack.infra.logsPage.toolbar.logFilterErrorToastTitle', + { + defaultMessage: 'Log filter error', + } +); diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts index 0fedcf2b2c74..fa0e5da4cb4c 100644 --- a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/index.ts @@ -5,5 +5,7 @@ * 2.0. */ +export * from './invalid_state_callout'; export * from './notification_channel'; export * from './send_actions'; +export * from './types'; diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/invalid_state_callout.tsx b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/invalid_state_callout.tsx new file mode 100644 index 000000000000..56f5f0355f74 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/invalid_state_callout.tsx @@ -0,0 +1,33 @@ +/* + * 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 { EuiCallOut } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import stringify from 'json-stable-stringify'; +import React from 'react'; +import type { State } from 'xstate'; + +export const InvalidStateCallout: React.FC<{ state: State }> = ({ + state, +}) => ( + + + +); + +const invalidStateCalloutTitle = i18n.translate( + 'xpack.infra.logs.common.invalidStateCalloutTitle', + { defaultMessage: 'Invalid state encountered' } +); diff --git a/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/types.ts b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/types.ts new file mode 100644 index 000000000000..05e75c5fe6e4 --- /dev/null +++ b/x-pack/plugins/infra/public/observability_logs/xstate_helpers/src/types.ts @@ -0,0 +1,43 @@ +/* + * 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 { ActorRef, ActorRefWithDeprecatedState, EmittedFrom, State, StateValue } from 'xstate'; + +export type OmitDeprecatedState> = Omit< + T, + 'state' +>; + +export type MatchedState< + TState extends State, + TStateValue extends StateValue +> = TState extends State< + any, + infer TEvent, + infer TStateSchema, + infer TTypestate, + infer TResolvedTypesMeta +> + ? State< + (TTypestate extends any + ? { value: TStateValue; context: any } extends TTypestate + ? TTypestate + : never + : never)['context'], + TEvent, + TStateSchema, + TTypestate, + TResolvedTypesMeta + > & { + value: TStateValue; + } + : never; + +export type MatchedStateFromActor< + TActorRef extends ActorRef, + TStateValue extends StateValue +> = MatchedState, TStateValue>; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx index 4d77077c19a9..f5a4ed2f9462 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_logs.tsx @@ -8,10 +8,9 @@ import { flowRight } from 'lodash'; import React from 'react'; import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom'; - -import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter'; import { replaceLogPositionInQueryString } from '../../containers/logs/log_position'; import { replaceSourceIdInQueryString } from '../../containers/source_id'; +import { replaceLogFilterInQueryString } from '../../observability_logs/log_stream_query_state'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; type RedirectToLogsType = RouteComponentProps<{}>; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx index afcb5200eb77..eb439e635886 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_logs.tsx @@ -6,19 +6,19 @@ */ import { i18n } from '@kbn/i18n'; +import { LinkDescriptor } from '@kbn/observability-plugin/public'; import { flowRight } from 'lodash'; import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; import useMount from 'react-use/lib/useMount'; -import { LinkDescriptor } from '@kbn/observability-plugin/public'; import { findInventoryFields } from '../../../common/inventory_models'; import { InventoryItemType } from '../../../common/inventory_models/types'; import { LoadingPage } from '../../components/loading_page'; -import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter'; import { replaceLogPositionInQueryString } from '../../containers/logs/log_position'; import { replaceSourceIdInQueryString } from '../../containers/source_id'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { useLogView } from '../../hooks/use_log_view'; +import { replaceLogFilterInQueryString } from '../../observability_logs/log_stream_query_state'; import { getFilterFromLocation, getTimeFromLocation } from './query_params'; type RedirectToNodeLogsType = RouteComponentProps<{ diff --git a/x-pack/plugins/infra/public/pages/logs/shared/page_template.tsx b/x-pack/plugins/infra/public/pages/logs/shared/page_template.tsx index 36efef518fa6..8ffe09acf950 100644 --- a/x-pack/plugins/infra/public/pages/logs/shared/page_template.tsx +++ b/x-pack/plugins/infra/public/pages/logs/shared/page_template.tsx @@ -12,7 +12,7 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import { NoDataConfig } from '@kbn/shared-ux-page-kibana-template'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; -interface LogsPageTemplateProps extends LazyObservabilityPageTemplateProps { +export interface LogsPageTemplateProps extends LazyObservabilityPageTemplateProps { hasData?: boolean; isDataLoading?: boolean; } diff --git a/x-pack/plugins/infra/public/pages/logs/stream/components/stream_page_template.tsx b/x-pack/plugins/infra/public/pages/logs/stream/components/stream_page_template.tsx new file mode 100644 index 000000000000..fb9c26676d01 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/logs/stream/components/stream_page_template.tsx @@ -0,0 +1,32 @@ +/* + * 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 { APP_WRAPPER_CLASS } from '@kbn/core/public'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { fullHeightContentStyles } from '../../../../page_template.styles'; +import { LogsPageTemplate, LogsPageTemplateProps } from '../../shared/page_template'; + +export const LogStreamPageTemplate: React.FC = React.memo((props) => ( +
+ +
+)); + +const streamTitle = i18n.translate('xpack.infra.logs.streamPageTitle', { + defaultMessage: 'Stream', +}); diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page.tsx index 540ff32949b6..562c0ef1b9c3 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page.tsx @@ -6,13 +6,16 @@ */ import { EuiErrorBoundary } from '@elastic/eui'; -import React from 'react'; import { useTrackPageview } from '@kbn/observability-plugin/public'; -import { useLogViewContext } from '../../../hooks/use_log_view'; +import React from 'react'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useLogsBreadcrumbs } from '../../../hooks/use_logs_breadcrumbs'; -import { ConnectedStreamPageContent } from './page_content'; -import { LogStreamPageProviders } from './page_providers'; +import { useLogViewContext } from '../../../hooks/use_log_view'; +import { LogStreamPageStateProvider } from '../../../observability_logs/log_stream_page/state'; import { streamTitle } from '../../../translations'; +import { useKbnUrlStateStorageFromRouterContext } from '../../../utils/kbn_url_state_context'; +import { useKibanaQuerySettings } from '../../../utils/use_kibana_query_settings'; +import { ConnectedStreamPageContent } from './page_content'; export const StreamPage = () => { useTrackPageview({ app: 'infra_logs', path: 'stream' }); @@ -25,12 +28,32 @@ export const StreamPage = () => { ]); const { logViewStateNotifications } = useLogViewContext(); + const { + services: { + data: { + query: { queryString: queryStringService, filterManager: filterManagerService }, + }, + notifications: { toasts: toastsService }, + }, + } = useKibanaContextForPlugin(); + + const kibanaQuerySettings = useKibanaQuerySettings(); + const urlStateStorage = useKbnUrlStateStorageFromRouterContext(); return ( - - - + + + ); }; + +const ConnectedStreamPageContentMemo = React.memo(ConnectedStreamPageContent); diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx index 44122164d8a9..485a099a7a6e 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_content.tsx @@ -5,104 +5,43 @@ * 2.0. */ -import { APP_WRAPPER_CLASS } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import { useSelector } from '@xstate/react'; +import { useActor } from '@xstate/react'; import React from 'react'; import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { useLogStreamPageStateContext } from '../../../observability_logs/log_stream_page/state/src/provider'; -import { fullHeightContentStyles } from '../../../page_template.styles'; +import { + LogStreamPageState, + useLogStreamPageStateContext, +} from '../../../observability_logs/log_stream_page/state'; +import { InvalidStateCallout } from '../../../observability_logs/xstate_helpers'; import { ConnectedLogViewErrorPage } from '../shared/page_log_view_error'; -import { LogsPageTemplate } from '../shared/page_template'; -import { LogsPageLogsContent } from './page_logs_content'; +import { StreamPageLogsContentForState } from './page_logs_content'; +import { StreamPageMissingIndicesContent } from './page_missing_indices_content'; import { LogStreamPageContentProviders } from './page_providers'; -const streamTitle = i18n.translate('xpack.infra.logs.streamPageTitle', { - defaultMessage: 'Stream', -}); - -interface InjectedProps { - isLoading: boolean; - hasFailedLoading: boolean; - hasIndices: boolean; - missingIndices: boolean; -} - export const ConnectedStreamPageContent: React.FC = () => { const logStreamPageStateService = useLogStreamPageStateContext(); - const isLoading = useSelector(logStreamPageStateService, (state) => { - return state.matches('uninitialized') || state.matches('loadingLogView'); - }); - - const hasFailedLoading = useSelector(logStreamPageStateService, (state) => - state.matches('loadingLogViewFailed') - ); - - const hasIndices = useSelector(logStreamPageStateService, (state) => - state.matches('hasLogViewIndices') - ); + const [logStreamPageState] = useActor(logStreamPageStateService); - const missingIndices = useSelector(logStreamPageStateService, (state) => - state.matches('missingLogViewIndices') - ); - - return ( - - ); + return ; }; -export const StreamPageContent: React.FC = (props: InjectedProps) => { - const { isLoading, hasFailedLoading, hasIndices, missingIndices } = props; - - if (isLoading) { +export const StreamPageContentForState: React.FC<{ logStreamPageState: LogStreamPageState }> = ({ + logStreamPageState, +}) => { + if (logStreamPageState.matches('uninitialized') || logStreamPageState.matches('loadingLogView')) { return ; - } else if (hasFailedLoading) { + } else if (logStreamPageState.matches('loadingLogViewFailed')) { return ; - } else if (missingIndices) { - return ( -
- -
- ); - } else if (hasIndices) { + } else if (logStreamPageState.matches('missingLogViewIndices')) { + return ; + } else if (logStreamPageState.matches({ hasLogViewIndices: 'initialized' })) { return ( -
- - - - - -
+ + + ); } else { - return null; + return ; } }; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx index 3b5956240d0e..bdfc4d1cb2cc 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_logs_content.tsx @@ -7,9 +7,9 @@ import { EuiSpacer } from '@elastic/eui'; import type { Query } from '@kbn/es-query'; +import { euiStyled } from '@kbn/kibana-react-plugin/common'; import React, { useCallback, useEffect, useMemo } from 'react'; import usePrevious from 'react-use/lib/usePrevious'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { LogEntry } from '../../../../common/log_entry'; import { TimeKey } from '../../../../common/time'; import { AutoSizer } from '../../../components/auto_sizer'; @@ -18,7 +18,6 @@ import { LogMinimap } from '../../../components/logging/log_minimap'; import { ScrollableLogTextStreamView } from '../../../components/logging/log_text_stream'; import { LogEntryStreamItem } from '../../../components/logging/log_text_stream/item'; import { PageContent } from '../../../components/page'; -import { useLogFilterStateContext } from '../../../containers/logs/log_filter'; import { useLogEntryFlyoutContext, WithFlyoutOptionsUrlState, @@ -30,15 +29,21 @@ import { WithSummary } from '../../../containers/logs/log_summary'; import { useLogViewConfigurationContext } from '../../../containers/logs/log_view_configuration'; import { useViewLogInProviderContext } from '../../../containers/logs/view_log_in_context'; import { WithLogTextviewUrlState } from '../../../containers/logs/with_log_textview'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useLogViewContext } from '../../../hooks/use_log_view'; +import { LogStreamPageActorRef } from '../../../observability_logs/log_stream_page/state'; +import { type ParsedQuery } from '../../../observability_logs/log_stream_query_state'; +import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; import { datemathToEpochMillis, isValidDatemath } from '../../../utils/datemath'; +import { LogStreamPageTemplate } from './components/stream_page_template'; import { LogsToolbar } from './page_toolbar'; import { PageViewLogInContext } from './page_view_log_in_context'; -import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; const PAGE_THRESHOLD = 2; -export const LogsPageLogsContent: React.FunctionComponent = () => { +export const StreamPageLogsContent = React.memo<{ + filterQuery: ParsedQuery; +}>(({ filterQuery }) => { const { data: { query: { queryString }, @@ -71,7 +76,6 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { updateDateRange, lastCompleteDateRangeExpressionUpdate, } = useLogPositionStateContext(); - const { filterQuery } = useLogFilterStateContext(); const { isReloading, @@ -210,7 +214,7 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { ); return ( - <> + @@ -276,9 +280,24 @@ export const LogsPageLogsContent: React.FunctionComponent = () => { }} - + ); -}; +}); + +type InitializedLogStreamPageState = MatchedStateFromActor< + LogStreamPageActorRef, + { hasLogViewIndices: 'initialized' } +>; + +export const StreamPageLogsContentForState = React.memo<{ + logStreamPageState: InitializedLogStreamPageState; +}>(({ logStreamPageState }) => { + const { + context: { parsedQuery }, + } = logStreamPageState; + + return ; +}); const LogPageMinimapColumn = euiStyled.div` flex: 1 0 0%; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_missing_indices_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_missing_indices_content.tsx new file mode 100644 index 000000000000..6d5ecf8ffd26 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_missing_indices_content.tsx @@ -0,0 +1,13 @@ +/* + * 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 React from 'react'; +import { LogStreamPageTemplate } from './components/stream_page_template'; + +export const StreamPageMissingIndicesContent = React.memo(() => ( + +)); diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx index fd716a116245..4ec53a9cd11e 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_providers.tsx @@ -5,12 +5,9 @@ * 2.0. */ -import React from 'react'; -import { - LogFilterStateProvider, - useLogFilterStateContext, - WithLogFilterUrlState, -} from '../../../containers/logs/log_filter'; +import stringify from 'json-stable-stringify'; +import React, { useMemo } from 'react'; +import { LogStreamPageActorRef } from '../../../observability_logs/log_stream_page/state'; import { LogEntryFlyoutProvider } from '../../../containers/logs/log_flyout'; import { LogHighlightsStateProvider } from '../../../containers/logs/log_highlights/log_highlights'; import { @@ -21,19 +18,7 @@ import { LogStreamProvider, useLogStreamContext } from '../../../containers/logs import { LogViewConfigurationProvider } from '../../../containers/logs/log_view_configuration'; import { ViewLogInContextProvider } from '../../../containers/logs/view_log_in_context'; import { useLogViewContext } from '../../../hooks/use_log_view'; -import { LogStreamPageStateProvider } from '../../../observability_logs/log_stream_page/state'; -import { type LogViewNotificationChannel } from '../../../observability_logs/log_view_state'; - -const LogFilterState: React.FC = ({ children }) => { - const { derivedDataView } = useLogViewContext(); - - return ( - - - {children} - - ); -}; +import { MatchedStateFromActor } from '../../../observability_logs/xstate_helpers'; const ViewLogInContext: React.FC = ({ children }) => { const { startTimestamp, endTimestamp } = useLogPositionStateContext(); @@ -54,10 +39,14 @@ const ViewLogInContext: React.FC = ({ children }) => { ); }; -const LogEntriesStateProvider: React.FC = ({ children }) => { +const LogEntriesStateProvider: React.FC<{ + logStreamPageState: InitializedLogStreamPageState; +}> = ({ children, logStreamPageState }) => { const { logViewId } = useLogViewContext(); const { startTimestamp, endTimestamp, targetPosition } = useLogPositionStateContext(); - const { filterQuery } = useLogFilterStateContext(); + const { + context: { parsedQuery }, + } = logStreamPageState; // Don't render anything if the date range is incorrect. if (!startTimestamp || !endTimestamp) { @@ -69,7 +58,7 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { sourceId={logViewId} startTimestamp={startTimestamp} endTimestamp={endTimestamp} - query={filterQuery?.parsedQuery} + query={parsedQuery} center={targetPosition ?? undefined} > {children} @@ -77,10 +66,15 @@ const LogEntriesStateProvider: React.FC = ({ children }) => { ); }; -const LogHighlightsState: React.FC = ({ children }) => { +const LogHighlightsState: React.FC<{ + logStreamPageState: InitializedLogStreamPageState; +}> = ({ children, logStreamPageState }) => { const { logViewId, logView } = useLogViewContext(); const { topCursor, bottomCursor, entries } = useLogStreamContext(); - const { filterQuery } = useLogFilterStateContext(); + const serializedParsedQuery = useMemo( + () => stringify(logStreamPageState.context.parsedQuery), + [logStreamPageState.context.parsedQuery] + ); const highlightsProps = { sourceId: logViewId, @@ -89,35 +83,32 @@ const LogHighlightsState: React.FC = ({ children }) => { entriesEnd: bottomCursor, centerCursor: entries.length > 0 ? entries[Math.floor(entries.length / 2)].cursor : null, size: entries.length, - filterQuery: filterQuery?.serializedQuery ?? null, + filterQuery: serializedParsedQuery, }; return {children}; }; -export const LogStreamPageProviders: React.FunctionComponent<{ - logViewStateNotifications: LogViewNotificationChannel; -}> = ({ children, logViewStateNotifications }) => { - return ( - - {children} - - ); -}; - -export const LogStreamPageContentProviders: React.FunctionComponent = ({ children }) => { +export const LogStreamPageContentProviders: React.FC<{ + logStreamPageState: InitializedLogStreamPageState; +}> = ({ children, logStreamPageState }) => { return ( - - - {children} - - + + + {children} + + ); }; + +type InitializedLogStreamPageState = MatchedStateFromActor< + LogStreamPageActorRef, + { hasLogViewIndices: 'initialized' } +>; diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx index 36c2349b471f..99c9d498fc6c 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_toolbar.tsx @@ -59,7 +59,7 @@ export const LogsToolbar = () => { indexPatterns={dataViews} showQueryInput={true} showQueryMenu={false} - showFilterBar={false} + showFilterBar={true} showDatePicker={true} displayStyle="inPage" /> diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ca749484c24c..ba4ecbd5dbc8 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -16804,7 +16804,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "Réessayer", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "Recherche d'entrées de log… (par ex. host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "Erreur de filtrage du log", - "xpack.infra.logsPage.toolbar.logFilterUnsupportedLanguageError": "SQL n'est pas pris en charge", "xpack.infra.logStream.kqlErrorTitle": "Expression KQL non valide", "xpack.infra.logStream.unknownErrorTitle": "Une erreur s'est produite", "xpack.infra.logStreamEmbeddable.description": "Ajoutez un tableau de logs de diffusion en direct.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e41d15377aa4..32819db411ac 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -16790,7 +16790,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "再試行", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "ログエントリーを検索中…(例:host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "ログフィルターエラー", - "xpack.infra.logsPage.toolbar.logFilterUnsupportedLanguageError": "SQLはサポートされていません", "xpack.infra.logStream.kqlErrorTitle": "無効なKQL式", "xpack.infra.logStream.unknownErrorTitle": "エラーが発生しました", "xpack.infra.logStreamEmbeddable.description": "ライブストリーミングログのテーブルを追加します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index efc9c2e106f8..e7e670652aac 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -16809,7 +16809,6 @@ "xpack.infra.logSourceErrorPage.tryAgainButtonLabel": "重试", "xpack.infra.logsPage.toolbar.kqlSearchFieldPlaceholder": "搜索日志条目……(例如 host.name:host-1)", "xpack.infra.logsPage.toolbar.logFilterErrorToastTitle": "日志筛选错误", - "xpack.infra.logsPage.toolbar.logFilterUnsupportedLanguageError": "不支持 SQL", "xpack.infra.logStream.kqlErrorTitle": "KQL 表达式无效", "xpack.infra.logStream.unknownErrorTitle": "发生错误", "xpack.infra.logStreamEmbeddable.description": "添加实时流式传输日志的表。", From c2c9cabfb7b48152d221262a16f0c37833df30a8 Mon Sep 17 00:00:00 2001 From: Coen Warmer Date: Tue, 3 Jan 2023 19:48:29 +0100 Subject: [PATCH 20/30] [SLO] Create SLO Edit Form - Custom KQL (#147843) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Closes https://github.com/elastic/kibana/issues/147753 --- packages/kbn-slo-schema/src/rest_specs/slo.ts | 13 +- .../kbn-slo-schema/src/schema/indicators.ts | 6 +- packages/kbn-slo-schema/src/schema/slo.ts | 4 +- x-pack/plugins/observability/dev_docs/slo.md | 128 +++---- .../observability/public/config/paths.ts | 2 + .../plugins/observability/public/data/slo.ts | 2 +- .../__storybook_mocks__/use_fetch_indices.ts | 18 + .../public/hooks/slo/use_create_slo.ts | 49 +++ .../public/hooks/slo/use_fetch_slo_details.ts | 7 +- .../public/hooks/slo/use_fetch_slo_list.ts | 10 +- .../public/hooks/use_fetch_indices.ts | 56 +++ .../public/pages/slo_details/index.tsx | 2 +- .../components/slo_edit_form.stories.tsx | 34 ++ .../slo_edit/components/slo_edit_form.tsx | 214 ++++++++++++ ...dit_form_definition_custom_kql.stories.tsx | 37 ++ .../slo_edit_form_definition_custom_kql.tsx | 166 +++++++++ .../slo_edit_form_description.stories.tsx | 34 ++ .../components/slo_edit_form_description.tsx | 87 +++++ .../slo_edit_form_objectives.stories.tsx | 34 ++ .../components/slo_edit_form_objectives.tsx | 131 +++++++ ...dit_form_objectives_timeslices.stories.tsx | 37 ++ .../slo_edit_form_objectives_timeslices.tsx | 75 ++++ .../public/pages/slo_edit/constants.ts | 45 +++ .../helpers/process_slo_form_values.ts | 36 ++ .../use_check_form_partial_validities.ts | 35 ++ .../public/pages/slo_edit/index.test.tsx | 322 ++++++++++++++++++ .../public/pages/slo_edit/index.tsx | 65 ++++ .../pages/slos/components/slo_list_item.tsx | 11 +- .../components/slo_list_welcome_prompt.tsx | 20 +- .../public/pages/slos/helpers/index.ts | 8 - .../public/pages/slos/index.test.tsx | 22 +- .../observability/public/pages/slos/index.tsx | 23 +- .../observability/public/routes/index.tsx | 15 + .../observability/server/routes/slo/route.ts | 4 +- .../server/services/slo/find_slo.test.ts | 16 +- .../server/services/slo/find_slo.ts | 8 +- .../server/services/slo/fixtures/slo.ts | 4 +- .../server/services/slo/get_slo.test.ts | 2 +- .../services/slo/slo_repository.test.ts | 8 +- .../services/slo/transform_manager.test.ts | 16 +- x-pack/plugins/observability/tsconfig.json | 1 + 41 files changed, 1661 insertions(+), 146 deletions(-) create mode 100644 x-pack/plugins/observability/public/hooks/__storybook_mocks__/use_fetch_indices.ts create mode 100644 x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts create mode 100644 x-pack/plugins/observability/public/hooks/use_fetch_indices.ts create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/constants.ts create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx create mode 100644 x-pack/plugins/observability/public/pages/slo_edit/index.tsx delete mode 100644 x-pack/plugins/observability/public/pages/slos/helpers/index.ts diff --git a/packages/kbn-slo-schema/src/rest_specs/slo.ts b/packages/kbn-slo-schema/src/rest_specs/slo.ts index 9a15a13e5bc2..629a7ac5b573 100644 --- a/packages/kbn-slo-schema/src/rest_specs/slo.ts +++ b/packages/kbn-slo-schema/src/rest_specs/slo.ts @@ -51,16 +51,16 @@ const getSLOParamsSchema = t.type({ }); const sortDirectionSchema = t.union([t.literal('asc'), t.literal('desc')]); -const sortBySchema = t.union([t.literal('name'), t.literal('indicator_type')]); +const sortBySchema = t.union([t.literal('name'), t.literal('indicatorType')]); const findSLOParamsSchema = t.partial({ query: t.partial({ name: t.string, - indicator_types: indicatorTypesArraySchema, + indicatorTypes: indicatorTypesArraySchema, page: t.string, - per_page: t.string, - sort_by: sortBySchema, - sort_direction: sortDirectionSchema, + perPage: t.string, + sortBy: sortBySchema, + sortDirection: sortDirectionSchema, }), }); @@ -123,6 +123,8 @@ type UpdateSLOResponse = t.OutputOf; type FindSLOParams = t.TypeOf; type FindSLOResponse = t.OutputOf; +type BudgetingMethod = t.TypeOf; + export { createSLOParamsSchema, deleteSLOParamsSchema, @@ -136,6 +138,7 @@ export { updateSLOResponseSchema, }; export type { + BudgetingMethod, CreateSLOParams, CreateSLOResponse, FindSLOParams, diff --git a/packages/kbn-slo-schema/src/schema/indicators.ts b/packages/kbn-slo-schema/src/schema/indicators.ts index 4781534b6d9b..73933fb78579 100644 --- a/packages/kbn-slo-schema/src/schema/indicators.ts +++ b/packages/kbn-slo-schema/src/schema/indicators.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { allOrAnyString, dateRangeSchema } from './common'; -const apmTransactionDurationIndicatorTypeSchema = t.literal('sli.apm.transaction_duration'); +const apmTransactionDurationIndicatorTypeSchema = t.literal('sli.apm.transactionDuration'); const apmTransactionDurationIndicatorSchema = t.type({ type: apmTransactionDurationIndicatorTypeSchema, params: t.intersection([ @@ -26,7 +26,7 @@ const apmTransactionDurationIndicatorSchema = t.type({ ]), }); -const apmTransactionErrorRateIndicatorTypeSchema = t.literal('sli.apm.transaction_error_rate'); +const apmTransactionErrorRateIndicatorTypeSchema = t.literal('sli.apm.transactionErrorRate'); const apmTransactionErrorRateIndicatorSchema = t.type({ type: apmTransactionErrorRateIndicatorTypeSchema, params: t.intersection([ @@ -69,7 +69,7 @@ const indicatorTypesSchema = t.union([ ]); // Validate that a string is a comma separated list of indicator types, -// e.g. sli.kql.custom,sli.apm.transaction_duration +// e.g. sli.kql.custom,sli.apm.transactionDuration // Transform to an array of indicator type const indicatorTypesArraySchema = new t.Type( 'indicatorTypesArray', diff --git a/packages/kbn-slo-schema/src/schema/slo.ts b/packages/kbn-slo-schema/src/schema/slo.ts index 5af9882593eb..91727fe4e997 100644 --- a/packages/kbn-slo-schema/src/schema/slo.ts +++ b/packages/kbn-slo-schema/src/schema/slo.ts @@ -12,8 +12,8 @@ import { durationType } from './duration'; import { indicatorSchema } from './indicators'; import { timeWindowSchema } from './time_window'; -const occurrencesBudgetingMethodSchema = t.literal('occurrences'); -const timeslicesBudgetingMethodSchema = t.literal('timeslices'); +const occurrencesBudgetingMethodSchema = t.literal('occurrences'); +const timeslicesBudgetingMethodSchema = t.literal('timeslices'); const budgetingMethodSchema = t.union([ occurrencesBudgetingMethodSchema, diff --git a/x-pack/plugins/observability/dev_docs/slo.md b/x-pack/plugins/observability/dev_docs/slo.md index 77c95fe26f42..a1a303a672af 100644 --- a/x-pack/plugins/observability/dev_docs/slo.md +++ b/x-pack/plugins/observability/dev_docs/slo.md @@ -1,10 +1,11 @@ -# SLO +# SLO Add the feature flag: `xpack.observability.unsafe.slo.enabled: true` in your Kibana config to enable the various SLO APIs. ## Supported SLI We currently support the following SLI: + - APM Transaction Error Rate (Availability) - APM Transaction Duration (Latency) - Custom KQL @@ -19,13 +20,13 @@ The **custom KQL** SLI requires an index pattern, an optional filter query, a nu We support **calendar aligned** and **rolling** time windows. Any duration greater than 1 day can be used: days, weeks, months, quarters, years. -**Rolling time window:** Requires a duration, e.g. `1w` for one week, and `is_rolling: true`. SLOs defined with such time window, will only considere the SLI data from the last duration period as a moving window. +**Rolling time window:** Requires a duration, e.g. `1w` for one week, and `isRolling: true`. SLOs defined with such time window, will only considere the SLI data from the last duration period as a moving window. -**Calendar aligned time window:** Requires a duration, e.g. `1M` for one month, and a `calendar.start_time` date in ISO 8601 in UTC, which marks the beginning of the first period. From start time and the duration, the system will compute the different time windows. For example, starting the calendar on the **01/01/2022** with a monthly duration, if today is the **24/10/2022**, the window associated is: `[2022-10-01T00:00:00Z, 2022-11-01T00:00:00Z]` +**Calendar aligned time window:** Requires a duration, e.g. `1M` for one month, and a `calendar.startTime` date in ISO 8601 in UTC, which marks the beginning of the first period. From start time and the duration, the system will compute the different time windows. For example, starting the calendar on the **01/01/2022** with a monthly duration, if today is the **24/10/2022**, the window associated is: `[2022-10-01T00:00:00Z, 2022-11-01T00:00:00Z]` ### Budgeting method -An SLO can be configured with an **occurrences** or **timeslices** budgeting method. +An SLO can be configured with an **occurrences** or **timeslices** budgeting method. An **occurrences** budgeting method uses the number of **good** and **total** events during the time window. @@ -33,16 +34,17 @@ A **timeslices** budgeting method uses the number of **good slices** and **total For example, defining a **timeslices** budgeting method with a `95%` slice threshold and `5m` slice window means that a 1 week SLO is split in 2,016 slices (`7*24*60 / 5`); for a 99% SLO target there will be approximately 20 minutes of available error budget. Each bucket is either good or bad depending on the ratio of good over total events during that bucket, compared to the slice threshold of 95%. -### Objective +### Objective The target objective is the value the SLO needs to meet during the time window. -If a **timeslices** budgeting method is used, we also need to define the **timeslice_target** which can be different than the overall SLO target. +If a **timeslices** budgeting method is used, we also need to define the **timesliceTarget** which can be different than the overall SLO target. ### Optional settings The default settings should be sufficient for most users, but if needed, the following properties can be overwritten: -- timestamp_field: The date time field to use from the source index -- sync_delay: The ingest delay in the source data + +- timestampField: The date time field to use from the source index +- syncDelay: The ingest delay in the source data - frequency: How often do we query the source data ## Example @@ -62,25 +64,26 @@ curl --request POST \ "name": "My SLO Name", "description": "My SLO Description", "indicator": { - "type": "sli.apm.transaction_error_rate", + "type": "sli.apm.transactionErrorRate", "params": { "environment": "production", "service": "o11y-app", - "transaction_type": "request", - "transaction_name": "GET /api", - "good_status_codes": ["2xx", "3xx", "4xx"] + "transactionType": "request", + "transactionName": "GET /api", + "goodStatusCodes": ["2xx", "3xx", "4xx"] } }, - "time_window": { + "timeWindow": { "duration": "30d", - "is_rolling": true + "isRolling": true }, - "budgeting_method": "occurrences", + "budgetingMethod": "occurrences", "objective": { "target": 0.99 } }' ``` +
@@ -96,27 +99,28 @@ curl --request POST \ "name": "My SLO Name", "description": "My SLO Description", "indicator": { - "type": "sli.apm.transaction_error_rate", + "type": "sli.apm.transactionErrorRate", "params": { "environment": "production", "service": "o11y-app", - "transaction_type": "request", - "transaction_name": "GET /api", - "good_status_codes": ["2xx", "3xx", "4xx"] + "transactionType": "request", + "transactionName": "GET /api", + "goodStatusCodes": ["2xx", "3xx", "4xx"] } }, - "time_window": { + "timeWindow": { "duration": "1q", "calendar": { - "start_time": "2022-06-01T00:00:00.000Z" + "startTime": "2022-06-01T00:00:00.000Z" } }, - "budgeting_method": "occurrences", + "budgetingMethod": "occurrences", "objective": { "target": 0.95 } }' ``` +
@@ -132,27 +136,28 @@ curl --request POST \ "name": "My SLO Name", "description": "My SLO Description", "indicator": { - "type": "sli.apm.transaction_error_rate", + "type": "sli.apm.transactionErrorRate", "params": { "environment": "production", "service": "o11y-app", - "transaction_type": "request", - "transaction_name": "GET /api", - "good_status_codes": ["2xx", "3xx", "4xx"] + "transactionType": "request", + "transactionName": "GET /api", + "goodStatusCodes": ["2xx", "3xx", "4xx"] } }, - "time_window": { + "timeWindow": { "duration": "1w", - "is_rolling": true + "isRolling": true }, - "budgeting_method": "timeslices", + "budgetingMethod": "timeslices", "objective": { "target": 0.90, - "timeslice_target": 0.86, - "timeslice_window": "5m" + "timesliceTarget": 0.86, + "timesliceWindow": "5m" } }' ``` +
### Latency @@ -170,25 +175,26 @@ curl --request POST \ "name": "My SLO Name", "description": "My SLO Description", "indicator": { - "type": "sli.apm.transaction_duration", + "type": "sli.apm.transactionDuration", "params": { "environment": "production", "service": "o11y-app", - "transaction_type": "request", - "transaction_name": "GET /api", + "transactionType": "request", + "transactionName": "GET /api", "threshold.us": 500000 } }, - "time_window": { + "timeWindow": { "duration": "7d", - "is_rolling": true + "isRolling": true }, - "budgeting_method": "occurrences", + "budgetingMethod": "occurrences", "objective": { "target": 0.99 } }' ``` +
@@ -204,29 +210,29 @@ curl --request POST \ "name": "My SLO Name", "description": "My SLO Description", "indicator": { - "type": "sli.apm.transaction_duration", + "type": "sli.apm.transactionDuration", "params": { "environment": "production", "service": "o11y-app", - "transaction_type": "request", - "transaction_name": "GET /api", + "transactionType": "request", + "transactionName": "GET /api", "threshold.us": 500000 } }, - "time_window": { + "timeWindow": { "duration": "7d", - "is_rolling": true + "isRolling": true }, - "budgeting_method": "timeslices", + "budgetingMethod": "timeslices", "objective": { "target": 0.95, - "timeslice_target": 0.90, - "timeslice_window": "1m" + "timesliceTarget": 0.90, + "timesliceWindow": "1m" } }' ``` -
+
99.9% of GET /api under 500ms weekly aligned (5m timeslices) @@ -241,35 +247,34 @@ curl --request POST \ "name": "My SLO Name", "description": "My SLO Description", "indicator": { - "type": "sli.apm.transaction_duration", + "type": "sli.apm.transactionDuration", "params": { "environment": "production", "service": "o11y-app", - "transaction_type": "request", - "transaction_name": "GET /api", + "transactionType": "request", + "transactionName": "GET /api", "threshold.us": 500000 } }, - "time_window": { + "timeWindow": { "duration": "7d", - "calendar": { - "start_time": "2022-01-01T00:00:00.000Z" + "calendar": { + "startTime": "2022-01-01T00:00:00.000Z" } }, - "budgeting_method": "timeslices", + "budgetingMethod": "timeslices", "objective": { "target": 0.999, - "timeslice_target": 0.95, - "timeslice_window": "5m" + "timesliceTarget": 0.95, + "timesliceWindow": "5m" } }' ``` -
+ ### Custom -
98.5% of 'logs lantency < 300ms' for 'groupId: group-0' over the last 7 days @@ -291,14 +296,15 @@ curl --request POST \ "filter": "labels.groupId: group-0" } }, - "time_window": { + "timeWindow": { "duration": "7d", - "is_rolling": true + "isRolling": true }, - "budgeting_method": "occurrences", + "budgetingMethod": "occurrences", "objective": { "target": 0.985 } }' ``` -
\ No newline at end of file + + diff --git a/x-pack/plugins/observability/public/config/paths.ts b/x-pack/plugins/observability/public/config/paths.ts index 76f621082bf3..73430af2edbc 100644 --- a/x-pack/plugins/observability/public/config/paths.ts +++ b/x-pack/plugins/observability/public/config/paths.ts @@ -18,6 +18,8 @@ export const paths = { ruleDetails: (ruleId?: string | null) => ruleId ? `${RULES_PAGE_LINK}/${encodeURI(ruleId)}` : RULES_PAGE_LINK, slos: SLOS_PAGE_LINK, + sloCreate: `${SLOS_PAGE_LINK}/create`, + sloEdit: (sloId: string) => `${SLOS_PAGE_LINK}/edit/${encodeURI(sloId)}`, sloDetails: (sloId: string) => `${SLOS_PAGE_LINK}/${encodeURI(sloId)}`, }, management: { diff --git a/x-pack/plugins/observability/public/data/slo.ts b/x-pack/plugins/observability/public/data/slo.ts index 3e225e22b533..73154263783a 100644 --- a/x-pack/plugins/observability/public/data/slo.ts +++ b/x-pack/plugins/observability/public/data/slo.ts @@ -25,7 +25,7 @@ const baseSlo: Omit = { index: 'some-index', filter: 'baz: foo and bar > 2', good: 'http_status: 2xx', - total: '', + total: 'a query', }, }, timeWindow: { diff --git a/x-pack/plugins/observability/public/hooks/__storybook_mocks__/use_fetch_indices.ts b/x-pack/plugins/observability/public/hooks/__storybook_mocks__/use_fetch_indices.ts new file mode 100644 index 000000000000..9127fcced23c --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/__storybook_mocks__/use_fetch_indices.ts @@ -0,0 +1,18 @@ +/* + * 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 { Index, UseFetchIndicesResponse } from '../use_fetch_indices'; + +export const useFetchIndices = (): UseFetchIndicesResponse => { + return { + loading: false, + error: false, + indices: Array.from({ length: 5 }, (_, i) => ({ + name: `.index${i}`, + })) as Index[], + }; +}; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts new file mode 100644 index 000000000000..77bf736ae46f --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/use_create_slo.ts @@ -0,0 +1,49 @@ +/* + * 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 { useCallback, useState } from 'react'; +import type { CreateSLOParams, CreateSLOResponse } from '@kbn/slo-schema'; + +import { useKibana } from '../../utils/kibana_react'; + +interface UseCreateSlo { + loading: boolean; + success: boolean; + error: string | undefined; + createSlo: (slo: CreateSLOParams) => void; +} + +export function useCreateSlo(): UseCreateSlo { + const { http } = useKibana().services; + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); + const [error, setError] = useState(undefined); + + const createSlo = useCallback( + async (slo: CreateSLOParams) => { + setLoading(true); + setError(''); + setSuccess(false); + const body = JSON.stringify(slo); + + try { + await http.post(`/api/observability/slos`, { body }); + setSuccess(true); + } catch (e) { + setError(e); + } + }, + [http] + ); + + return { + loading, + error, + success, + createSlo, + }; +} diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts index 722937bbe0de..83da5b9f5ac7 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_details.ts @@ -10,12 +10,12 @@ import { useCallback, useMemo } from 'react'; import { GetSLOResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useDataFetcher } from '../use_data_fetcher'; -interface UseFetchSloDetailsResponse { +export interface UseFetchSloDetailsResponse { loading: boolean; slo: SLOWithSummaryResponse | undefined; } -function useFetchSloDetails(sloId?: string): UseFetchSloDetailsResponse { +export function useFetchSloDetails(sloId?: string): UseFetchSloDetailsResponse { const params = useMemo(() => ({ sloId }), [sloId]); const shouldExecuteApiCall = useCallback( (apiCallParams: { sloId?: string }) => params.sloId === apiCallParams.sloId, @@ -57,6 +57,3 @@ const fetchSlo = async ( return undefined; }; - -export type { UseFetchSloDetailsResponse }; -export { useFetchSloDetails }; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts index f2fdf45a6e75..2b4d56c701c7 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts @@ -37,12 +37,8 @@ export function useFetchSloList({ refetch, sortBy, indicatorTypes, -}: { +}: SLOListParams & { refetch: boolean; - name?: string; - page?: number; - sortBy?: string; - indicatorTypes?: string[]; }): UseFetchSloListResponse { const [sloList, setSloList] = useState(EMPTY_LIST); @@ -84,9 +80,9 @@ const fetchSloList = async ( query: { ...(params.page && { page: params.page }), ...(params.name && { name: params.name }), - ...(params.sortBy && { sort_by: params.sortBy }), + ...(params.sortBy && { sortBy: params.sortBy }), ...(params.indicatorTypes && - params.indicatorTypes.length > 0 && { indicator_types: params.indicatorTypes.join(',') }), + params.indicatorTypes.length > 0 && { indicatorTypes: params.indicatorTypes.join(',') }), }, signal: abortController.signal, }); diff --git a/x-pack/plugins/observability/public/hooks/use_fetch_indices.ts b/x-pack/plugins/observability/public/hooks/use_fetch_indices.ts new file mode 100644 index 000000000000..42be87704993 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/use_fetch_indices.ts @@ -0,0 +1,56 @@ +/* + * 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 { HttpSetup } from '@kbn/core/public'; +import { useRef } from 'react'; +import { useDataFetcher } from './use_data_fetcher'; + +export interface UseFetchIndicesResponse { + indices: Index[]; + loading: boolean; + error: boolean; +} + +export interface Index { + name: string; +} + +export function useFetchIndices(): UseFetchIndicesResponse { + const hasFetched = useRef(false); + + const { + data: indices, + loading, + error, + } = useDataFetcher({ + paramsForApiCall: {}, + initialDataState: undefined, + executeApiCall: async ( + _: any, + abortController: AbortController, + http: HttpSetup + ): Promise => { + try { + const response = await http.get(`/api/index_management/indices`, { + signal: abortController.signal, + }); + + if (response !== undefined) { + hasFetched.current = true; + return response; + } + } catch (e) { + // ignore error for retrieving slos + } + + return; + }, + shouldExecuteApiCall: () => (hasFetched.current === false ? true : false), + }); + + return { indices, loading, error }; +} diff --git a/x-pack/plugins/observability/public/pages/slo_details/index.tsx b/x-pack/plugins/observability/public/pages/slo_details/index.tsx index 2e18597e228c..9da17bb0ec5f 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/index.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/index.tsx @@ -18,7 +18,7 @@ import { usePluginContext } from '../../hooks/use_plugin_context'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useKibana } from '../../utils/kibana_react'; import PageNotFound from '../404'; -import { isSloFeatureEnabled } from '../slos/helpers'; +import { isSloFeatureEnabled } from '../slos/helpers/is_slo_feature_enabled'; import { SLOS_BREADCRUMB_TEXT } from '../slos/translations'; import { SloDetailsPathParams } from './types'; import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.stories.tsx new file mode 100644 index 000000000000..ace1088d4a3b --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.stories.tsx @@ -0,0 +1,34 @@ +/* + * 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 React from 'react'; +import { ComponentStory } from '@storybook/react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; +import { SloEditForm as Component, Props } from './slo_edit_form'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/SloEditForm', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps = {}; + +export const SloEditForm = Template.bind({}); +SloEditForm.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx new file mode 100644 index 000000000000..723f4e01af90 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form.tsx @@ -0,0 +1,214 @@ +/* + * 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 React from 'react'; +import { + EuiAvatar, + EuiButton, + EuiFormLabel, + EuiPanel, + EuiSelect, + EuiSpacer, + EuiTimeline, + EuiTimelineItem, + EuiTitle, +} from '@elastic/eui'; +import { euiThemeVars } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; +import { Controller, useForm } from 'react-hook-form'; +import type { SLOWithSummaryResponse } from '@kbn/slo-schema'; + +import { useKibana } from '../../../utils/kibana_react'; +import { useCreateSlo } from '../../../hooks/slo/use_create_slo'; +import { useCheckFormPartialValidities } from '../helpers/use_check_form_partial_validities'; +import { SloEditFormDefinitionCustomKql } from './slo_edit_form_definition_custom_kql'; +import { SloEditFormDescription } from './slo_edit_form_description'; +import { SloEditFormObjectives } from './slo_edit_form_objectives'; +import { + processValues, + transformGetSloToCreateSloParams, +} from '../helpers/process_slo_form_values'; +import { paths } from '../../../config'; +import { SLI_OPTIONS, SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; + +export interface Props { + slo: SLOWithSummaryResponse | undefined; +} + +const maxWidth = 775; + +export function SloEditForm({ slo }: Props) { + const { + application: { navigateToUrl }, + http: { basePath }, + notifications: { toasts }, + } = useKibana().services; + + const { control, watch, getFieldState, getValues, formState, trigger } = useForm({ + defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES, + values: transformGetSloToCreateSloParams(slo), + mode: 'all', + }); + + const { isDefinitionValid, isDescriptionValid, isObjectiveValid } = useCheckFormPartialValidities( + { getFieldState, formState } + ); + + const { + loading: loadingCreatingSlo, + success: successCreatingSlo, + error: errorCreatingSlo, + createSlo, + } = useCreateSlo(); + + const handleCreateSlo = () => { + const values = getValues(); + const processedValues = processValues(values); + createSlo(processedValues); + }; + + if (successCreatingSlo) { + toasts.addSuccess( + i18n.translate('xpack.observability.slos.sloEdit.creation.success', { + defaultMessage: 'Successfully created {name}', + values: { name: getValues().name }, + }) + ); + navigateToUrl(basePath.prepend(paths.observability.slos)); + } + + if (errorCreatingSlo) { + toasts.addError(new Error(errorCreatingSlo), { + title: i18n.translate('xpack.observability.slos.sloEdit.creation.error', { + defaultMessage: 'Something went wrong', + }), + }); + } + + return ( + + + } + > + + +

+ {i18n.translate('xpack.observability.slos.sloEdit.definition.title', { + defaultMessage: 'Define SLI', + })} +

+
+ + + + + {i18n.translate('xpack.observability.slos.sloEdit.definition.sliType', { + defaultMessage: 'SLI type', + })} + + + ( + + )} + /> + + + + {watch('indicator.type') === 'sli.kql.custom' ? ( + + ) : null} + + +
+
+ + + } + > + + +

+ {i18n.translate('xpack.observability.slos.sloEdit.objectives.title', { + defaultMessage: 'Set objectives', + })} +

+
+ + + + + + +
+
+ + + } + > + + +

+ {i18n.translate('xpack.observability.slos.sloEdit.description.title', { + defaultMessage: 'Describe SLO', + })} +

+
+ + + + + + + + + {i18n.translate('xpack.observability.slos.sloEdit.createSloButton', { + defaultMessage: 'Create SLO', + })} + + + +
+
+
+ ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.stories.tsx new file mode 100644 index 000000000000..08c634b45faa --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.stories.tsx @@ -0,0 +1,37 @@ +/* + * 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 React from 'react'; +import { ComponentStory } from '@storybook/react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; +import { + SloEditFormDefinitionCustomKql as Component, + Props, +} from './slo_edit_form_definition_custom_kql'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/SloEditFormDefinitionCustomKql', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps = {}; + +export const SloEditFormDefinitionCustomKql = Template.bind({}); +SloEditFormDefinitionCustomKql.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx new file mode 100644 index 000000000000..dae887c595e7 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_definition_custom_kql.tsx @@ -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 React, { useEffect } from 'react'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiSuggest } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Control, Controller, UseFormTrigger } from 'react-hook-form'; +import type { CreateSLOParams } from '@kbn/slo-schema'; + +import { useFetchIndices } from '../../../hooks/use_fetch_indices'; + +export interface Props { + control: Control; + trigger: UseFormTrigger; +} + +export function SloEditFormDefinitionCustomKql({ control, trigger }: Props) { + const { loading, indices = [] } = useFetchIndices(); + + const indicesNames = indices.map(({ name }) => ({ + type: { iconType: '', color: '' }, + label: name, + description: '', + })); + + // Indices are loading in asynchrously, so trigger field validation + // once results are returned from API + useEffect(() => { + if (!loading && indices.length) { + trigger(); + } + }, [indices.length, loading, trigger]); + + return ( + + + + {i18n.translate('xpack.observability.slos.sloEdit.sloDefinition.customKql.index', { + defaultMessage: 'Index', + })} + + + Boolean(indices.find((index) => index.name === value)), + }} + render={({ field }) => ( + { + field.onChange(label); + }} + isInvalid={!Boolean(indicesNames.find((index) => index.label === field.value))} + placeholder={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.index.selectIndex', + { + defaultMessage: 'Select an index', + } + )} + suggestions={indicesNames} + {...field} + /> + )} + /> + + + + + {i18n.translate('xpack.observability.slos.sloEdit.sloDefinition.customKql.queryFilter', { + defaultMessage: 'Query filter', + })} + + ( + KQL} + status="unchanged" + aria-label="Filter query" + data-test-subj="sloFormCustomKqlFilterQueryInput" + placeholder={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.customFilter', + { + defaultMessage: 'Custom filter to apply on the index', + } + )} + suggestions={[]} + {...field} + /> + )} + /> + + + + + {i18n.translate('xpack.observability.slos.sloEdit.sloDefinition.customKql.goodQuery', { + defaultMessage: 'Good query', + })} + + ( + KQL} + status="unchanged" + aria-label="Good filter" + data-test-subj="sloFormCustomKqlGoodQueryInput" + placeholder={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.goodQueryPlaceholder', + { + defaultMessage: 'Define the good events', + } + )} + suggestions={[]} + {...field} + /> + )} + /> + + + + + {i18n.translate('xpack.observability.slos.sloEdit.sloDefinition.customKql.totalQuery', { + defaultMessage: 'Total query', + })} + + ( + KQL} + status="unchanged" + aria-label="Total filter" + data-test-subj="sloFormCustomKqlTotalQueryInput" + placeholder={i18n.translate( + 'xpack.observability.slos.sloEdit.sloDefinition.customKql.totalQueryPlaceholder', + { + defaultMessage: 'Define the total events', + } + )} + suggestions={[]} + {...field} + /> + )} + /> + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx new file mode 100644 index 000000000000..42b6d674b2f6 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.stories.tsx @@ -0,0 +1,34 @@ +/* + * 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 React from 'react'; +import { ComponentStory } from '@storybook/react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; +import { SloEditFormDescription as Component, Props } from './slo_edit_form_description'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/SloEditFormDescription', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps = {}; + +export const SloEditFormDescription = Template.bind({}); +SloEditFormDescription.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx new file mode 100644 index 000000000000..c8442d5c5d56 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_description.tsx @@ -0,0 +1,87 @@ +/* + * 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 { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormLabel, + EuiTextArea, + useGeneratedHtmlId, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { Control, Controller } from 'react-hook-form'; +import type { CreateSLOParams } from '@kbn/slo-schema'; + +export interface Props { + control: Control; +} + +export function SloEditFormDescription({ control }: Props) { + const sloNameId = useGeneratedHtmlId({ prefix: 'sloName' }); + const descriptionId = useGeneratedHtmlId({ prefix: 'sloDescription' }); + + return ( + + + + {i18n.translate('xpack.observability.slos.sloEdit.description.sloName', { + defaultMessage: 'SLO Name', + })} + + + ( + + )} + /> + + + + + {i18n.translate('xpack.observability.slos.sloEdit.description.sloDescription', { + defaultMessage: 'Description', + })} + + + ( + + )} + /> + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx new file mode 100644 index 000000000000..3540c71fc231 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.stories.tsx @@ -0,0 +1,34 @@ +/* + * 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 React from 'react'; +import { ComponentStory } from '@storybook/react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; +import { SloEditFormObjectives as Component, Props } from './slo_edit_form_objectives'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/SloEditFormObjectives', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps = {}; + +export const SloEditFormObjectives = Template.bind({}); +SloEditFormObjectives.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx new file mode 100644 index 000000000000..6f32b8d749cb --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives.tsx @@ -0,0 +1,131 @@ +/* + * 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 React from 'react'; +import { + EuiFieldNumber, + EuiFlexGrid, + EuiFlexItem, + EuiFormLabel, + EuiSelect, + EuiSpacer, + useGeneratedHtmlId, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Control, Controller, UseFormWatch } from 'react-hook-form'; +import type { BudgetingMethod, CreateSLOParams } from '@kbn/slo-schema'; + +import { SloEditFormObjectivesTimeslices } from './slo_edit_form_objectives_timeslices'; + +export const BUDGETING_METHOD_OPTIONS: Array<{ value: BudgetingMethod; text: string }> = [ + { value: 'occurrences', text: 'Occurences' }, + { value: 'timeslices', text: 'Timeslices' }, +]; + +export const TIMEWINDOW_OPTIONS = [30, 7].map((number) => ({ + value: `${number}d`, + text: i18n.translate('xpack.observability.slos.sloEdit.objectives.days', { + defaultMessage: '{number} days', + values: { number }, + }), +})); + +export interface Props { + control: Control; + watch: UseFormWatch; +} + +export function SloEditFormObjectives({ control, watch }: Props) { + const budgetingSelect = useGeneratedHtmlId({ prefix: 'budgetingSelect' }); + const timeWindowSelect = useGeneratedHtmlId({ prefix: 'timeWindowSelect' }); + + return ( + <> + + + + {i18n.translate('xpack.observability.slos.sloEdit.objectives.budgetingMethod', { + defaultMessage: 'Budgeting method', + })} + + + ( + + )} + /> + + + + + {i18n.translate('xpack.observability.slos.sloEdit.objectives.timeWindow', { + defaultMessage: 'Time window', + })} + + + ( + + )} + /> + + + + + {i18n.translate('xpack.observability.slos.sloEdit.objectives.targetSlo', { + defaultMessage: 'Target / SLO (%)', + })} + + + ( + field.onChange(Number(event.target.value))} + /> + )} + /> + + + + {watch('budgetingMethod') === 'timeslices' ? ( + <> + + + + ) : null} + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx new file mode 100644 index 000000000000..7bdd33cf741b --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.stories.tsx @@ -0,0 +1,37 @@ +/* + * 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 React from 'react'; +import { ComponentStory } from '@storybook/react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator'; +import { + SloEditFormObjectivesTimeslices as Component, + Props, +} from './slo_edit_form_objectives_timeslices'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from '../constants'; + +export default { + component: Component, + title: 'app/SLO/EditPage/SloEditFormObjectivesTimeslices', + decorators: [KibanaReactStorybookDecorator], +}; + +const Template: ComponentStory = (props: Props) => { + const methods = useForm({ defaultValues: SLO_EDIT_FORM_DEFAULT_VALUES }); + return ( + + + + ); +}; + +const defaultProps = {}; + +export const SloEditFormObjectivesTimeslices = Template.bind({}); +SloEditFormObjectivesTimeslices.args = defaultProps; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx new file mode 100644 index 000000000000..9f8542e65fe1 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/slo_edit_form_objectives_timeslices.tsx @@ -0,0 +1,75 @@ +/* + * 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 React from 'react'; +import { EuiFieldNumber, EuiFlexGrid, EuiFlexItem, EuiFormLabel } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { Control, Controller } from 'react-hook-form'; +import type { CreateSLOParams } from '@kbn/slo-schema'; + +export interface Props { + control: Control; +} + +export function SloEditFormObjectivesTimeslices({ control }: Props) { + return ( + + + + {i18n.translate('xpack.observability.slos.sloEdit.objectives.timeSliceTarget', { + defaultMessage: 'Timeslice target (%)', + })} + + ( + field.onChange(Number(event.target.value))} + /> + )} + /> + + + + + {i18n.translate('xpack.observability.slos.sloEdit.objectives.timesliceWindow', { + defaultMessage: 'Timeslice window (minutes)', + })} + + + ( + field.onChange(String(event.target.value))} + /> + )} + /> + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/constants.ts b/x-pack/plugins/observability/public/pages/slo_edit/constants.ts new file mode 100644 index 000000000000..fc036f5ba6ee --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/constants.ts @@ -0,0 +1,45 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import type { CreateSLOParams } from '@kbn/slo-schema'; + +import { + BUDGETING_METHOD_OPTIONS, + TIMEWINDOW_OPTIONS, +} from './components/slo_edit_form_objectives'; + +export const SLI_OPTIONS = [ + { + value: 'sli.kql.custom' as const, + text: i18n.translate('xpack.observability.slos.sloTypes.kqlCustomIndicator', { + defaultMessage: 'KQL custom indicator', + }), + }, +]; + +export const SLO_EDIT_FORM_DEFAULT_VALUES: CreateSLOParams = { + name: '', + description: '', + indicator: { + type: SLI_OPTIONS[0].value, + params: { + index: '', + filter: '', + good: '', + total: '', + }, + }, + timeWindow: { + duration: TIMEWINDOW_OPTIONS[0].value as any, // Get this to be a proper Duration + isRolling: true, + }, + budgetingMethod: BUDGETING_METHOD_OPTIONS[0].value, + objective: { + target: 99.5, + }, +}; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts b/x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts new file mode 100644 index 000000000000..35ad905d46fe --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/helpers/process_slo_form_values.ts @@ -0,0 +1,36 @@ +/* + * 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 { CreateSLOParams, GetSLOResponse } from '@kbn/slo-schema'; + +export function transformGetSloToCreateSloParams( + values: GetSLOResponse | undefined +): CreateSLOParams | undefined { + if (!values) return undefined; + + return { + ...values, + objective: { + target: values.objective.target * 100, + ...(values.objective.timesliceTarget && { + timesliceTarget: values.objective.timesliceTarget * 100, + }), + }, + } as unknown as CreateSLOParams; +} + +export function processValues(values: CreateSLOParams): CreateSLOParams { + return { + ...values, + objective: { + target: values.objective.target / 100, + ...(values.objective.timesliceTarget && { + timesliceTarget: values.objective.timesliceTarget / 100, + }), + }, + }; +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts b/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts new file mode 100644 index 000000000000..5ae5d5b7fa28 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/helpers/use_check_form_partial_validities.ts @@ -0,0 +1,35 @@ +/* + * 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 { FormState, UseFormGetFieldState } from 'react-hook-form'; +import type { CreateSLOParams } from '@kbn/slo-schema'; + +interface Props { + getFieldState: UseFormGetFieldState; + formState: FormState; +} + +export function useCheckFormPartialValidities({ getFieldState, formState }: Props) { + const isDefinitionValid = ( + [ + 'indicator.params.index', + 'indicator.params.filter', + 'indicator.params.good', + 'indicator.params.total', + ] as const + ).every((field) => getFieldState(field, formState).error === undefined); + + const isObjectiveValid = ( + ['budgetingMethod', 'timeWindow.duration', 'objective.target'] as const + ).every((field) => getFieldState(field, formState).error === undefined); + + const isDescriptionValid = (['name', 'description'] as const).every( + (field) => getFieldState(field, formState).error === undefined + ); + + return { isDefinitionValid, isObjectiveValid, isDescriptionValid }; +} diff --git a/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx b/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx new file mode 100644 index 000000000000..816693cd6a9f --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/index.test.tsx @@ -0,0 +1,322 @@ +/* + * 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 React from 'react'; +import Router from 'react-router-dom'; +import { waitFor, fireEvent, screen } from '@testing-library/dom'; +import { BasePath } from '@kbn/core-http-server-internal'; +import { cleanup } from '@testing-library/react'; + +import { render } from '../../utils/test_helper'; +import { useKibana } from '../../utils/kibana_react'; +import { useFetchIndices } from '../../hooks/use_fetch_indices'; +import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; +import { useCreateSlo } from '../../hooks/slo/use_create_slo'; +import { kibanaStartMock } from '../../utils/kibana_react.mock'; +import { ConfigSchema } from '../../plugin'; +import { Subset } from '../../typings'; +import { SLO_EDIT_FORM_DEFAULT_VALUES } from './constants'; +import { anSLO } from '../../data/slo'; +import { paths } from '../../config'; +import { SloEditPage } from '.'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: jest.fn(), +})); + +jest.mock('../../hooks/use_breadcrumbs'); +jest.mock('../../hooks/use_fetch_indices'); +jest.mock('../../hooks/slo/use_fetch_slo_details'); +jest.mock('../../hooks/slo/use_create_slo'); + +const mockUseKibanaReturnValue = kibanaStartMock.startContract(); + +jest.mock('../../utils/kibana_react', () => ({ + useKibana: jest.fn(() => mockUseKibanaReturnValue), +})); + +const useFetchIndicesMock = useFetchIndices as jest.Mock; +const useFetchSloMock = useFetchSloDetails as jest.Mock; +const useCreateSloMock = useCreateSlo as jest.Mock; + +const mockAddSuccess = jest.fn(); +const mockAddError = jest.fn(); +const mockNavigate = jest.fn(); + +(useKibana as jest.Mock).mockReturnValue({ + services: { + application: { navigateToUrl: mockNavigate }, + http: { + basePath: new BasePath('', undefined), + }, + notifications: { + toasts: { + addSuccess: mockAddSuccess, + addError: mockAddError, + }, + }, + }, +}); + +const config: Subset = { + unsafe: { + slo: { enabled: true }, + }, +}; + +describe('SLO Edit Page', () => { + beforeEach(() => { + jest.clearAllMocks(); + + // Silence all the ref errors in Eui components. + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + afterEach(cleanup); + + describe('when the feature flag is not enabled', () => { + it('renders the not found page when no sloId param is passed', async () => { + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); + + useFetchSloMock.mockReturnValue({ loading: false, slo: undefined }); + + render(, { unsafe: { slo: { enabled: false } } }); + + expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); + }); + + it('renders the not found page when sloId param is passed', async () => { + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '1234' }); + + useFetchSloMock.mockReturnValue({ loading: false, slo: undefined }); + + render(, { unsafe: { slo: { enabled: false } } }); + + expect(screen.queryByTestId('pageNotFound')).toBeTruthy(); + }); + }); + + describe('when the feature flag is enabled', () => { + it('renders the SLO Edit page in pristine state when no sloId route param is passed', async () => { + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); + + useFetchSloMock.mockReturnValue({ loading: false, slo: undefined }); + useFetchIndicesMock.mockReturnValue({ + loading: false, + indices: [{ name: 'some-index' }], + }); + useCreateSloMock.mockReturnValue({ + loading: false, + success: false, + error: '', + createSlo: jest.fn(), + }); + + render(, config); + + expect(screen.queryByTestId('slosEditPage')).toBeTruthy(); + expect(screen.queryByTestId('sloForm')).toBeTruthy(); + + expect(screen.queryByTestId('sloFormIndicatorTypeSelect')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type + ); + + expect(screen.queryByTestId('sloFormCustomKqlIndexInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.index + ); + expect(screen.queryByTestId('sloFormCustomKqlFilterQueryInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' + ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.filter + : '' + ); + expect(screen.queryByTestId('sloFormCustomKqlGoodQueryInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' + ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.good + : '' + ); + expect(screen.queryByTestId('sloFormCustomKqlTotalQueryInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.indicator.type === 'sli.kql.custom' + ? SLO_EDIT_FORM_DEFAULT_VALUES.indicator.params.total + : '' + ); + + expect(screen.queryByTestId('sloFormBudgetingMethodSelect')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.budgetingMethod + ); + expect(screen.queryByTestId('sloFormTimeWindowDurationSelect')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.timeWindow.duration as any + ); + expect(screen.queryByTestId('sloFormObjectiveTargetInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.objective.target + ); + + expect(screen.queryByTestId('sloFormNameInput')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.name + ); + expect(screen.queryByTestId('sloFormDescriptionTextArea')).toHaveValue( + SLO_EDIT_FORM_DEFAULT_VALUES.description + ); + }); + + it('renders the SLO Edit page with prefilled form values if sloId route param is passed', async () => { + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchSloMock.mockReturnValue({ loading: false, slo: anSLO }); + useFetchIndicesMock.mockReturnValue({ + loading: false, + indices: [{ name: 'some-index' }], + }); + useCreateSloMock.mockReturnValue({ + loading: false, + success: false, + error: '', + createSlo: jest.fn(), + }); + render(, config); + + expect(screen.queryByTestId('slosEditPage')).toBeTruthy(); + expect(screen.queryByTestId('sloForm')).toBeTruthy(); + + expect(screen.queryByTestId('sloFormIndicatorTypeSelect')).toHaveValue(anSLO.indicator.type); + + expect(screen.queryByTestId('sloFormCustomKqlIndexInput')).toHaveValue( + anSLO.indicator.params.index + ); + expect(screen.queryByTestId('sloFormCustomKqlFilterQueryInput')).toHaveValue( + anSLO.indicator.type === 'sli.kql.custom' ? anSLO.indicator.params.filter : '' + ); + expect(screen.queryByTestId('sloFormCustomKqlGoodQueryInput')).toHaveValue( + anSLO.indicator.type === 'sli.kql.custom' ? anSLO.indicator.params.good : '' + ); + expect(screen.queryByTestId('sloFormCustomKqlTotalQueryInput')).toHaveValue( + anSLO.indicator.type === 'sli.kql.custom' ? anSLO.indicator.params.total : '' + ); + + expect(screen.queryByTestId('sloFormBudgetingMethodSelect')).toHaveValue( + anSLO.budgetingMethod + ); + expect(screen.queryByTestId('sloFormTimeWindowDurationSelect')).toHaveValue( + anSLO.timeWindow.duration + ); + expect(screen.queryByTestId('sloFormObjectiveTargetInput')).toHaveValue( + anSLO.objective.target * 100 + ); + + expect(screen.queryByTestId('sloFormNameInput')).toHaveValue(anSLO.name); + expect(screen.queryByTestId('sloFormDescriptionTextArea')).toHaveValue(anSLO.description); + }); + + it('enables submitting if all required values are filled in', async () => { + // Note: the `anSLO` object is considered to have (at least) + // values for all required fields. + + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + + useFetchIndicesMock.mockReturnValue({ + loading: false, + indices: [{ name: 'some-index' }], + }); + useFetchSloMock.mockReturnValue({ loading: false, slo: anSLO }); + const mockCreate = jest.fn(); + useCreateSloMock.mockReturnValue({ + loading: false, + success: false, + error: '', + createSlo: mockCreate, + }); + + render(, config); + + await waitFor(() => expect(screen.queryByTestId('sloFormSubmitButton')).toBeEnabled()); + + fireEvent.click(screen.queryByTestId('sloFormSubmitButton')!); + + expect(mockCreate).toBeCalledWith(anSLO); + }); + + it('blocks submitting if not all required values are filled in', async () => { + // Note: the `anSLO` object is considered to have (at least) + // values for all required fields. + + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + useFetchIndicesMock.mockReturnValue({ + loading: false, + indices: [], + }); + + useFetchSloMock.mockReturnValue({ loading: false, slo: { ...anSLO, name: '' } }); + + render(, config); + + await waitFor(() => { + expect(screen.queryByTestId('sloFormSubmitButton')).toBeDisabled(); + }); + }); + + describe('if submitting has completed successfully', () => { + it('renders a success toast', async () => { + // Note: the `anSLO` object is considered to have (at least) + // values for all required fields. + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + useFetchSloMock.mockReturnValue({ loading: false, slo: anSLO }); + useFetchIndicesMock.mockReturnValue({ + loading: false, + indices: [{ name: 'some-index' }], + }); + useCreateSloMock.mockReturnValue({ + loading: false, + success: true, + error: '', + createSlo: jest.fn(), + }); + render(, config); + expect(mockAddSuccess).toBeCalled(); + }); + + it('navigates to the SLO List page', async () => { + // Note: the `anSLO` object is considered to have (at least) + // values for all required fields. + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + useFetchSloMock.mockReturnValue({ loading: false, slo: anSLO }); + useFetchIndicesMock.mockReturnValue({ + loading: false, + indices: [{ name: 'some-index' }], + }); + useCreateSloMock.mockReturnValue({ + loading: false, + success: true, + error: '', + createSlo: jest.fn(), + }); + render(, config); + expect(mockNavigate).toBeCalledWith(paths.observability.slos); + }); + }); + + describe('if submitting has not completed successfully', () => { + it('renders an error toast', async () => { + // Note: the `anSLO` object is considered to have (at least) + // values for all required fields. + jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); + useFetchSloMock.mockReturnValue({ loading: false, slo: anSLO }); + useFetchIndicesMock.mockReturnValue({ + loading: false, + indices: [{ name: 'some-index' }], + }); + useCreateSloMock.mockReturnValue({ + loading: false, + success: false, + error: 'Argh, API died', + createSlo: jest.fn(), + }); + render(, config); + expect(mockAddError).toBeCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/index.tsx b/x-pack/plugins/observability/public/pages/slo_edit/index.tsx new file mode 100644 index 000000000000..d27390d2447f --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_edit/index.tsx @@ -0,0 +1,65 @@ +/* + * 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 React from 'react'; +import { useParams } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; + +import { ObservabilityAppServices } from '../../application/types'; +import { paths } from '../../config'; +import { usePluginContext } from '../../hooks/use_plugin_context'; +import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useKibana } from '../../utils/kibana_react'; +import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; +import { SloEditForm } from './components/slo_edit_form'; +import PageNotFound from '../404'; +import { isSloFeatureEnabled } from '../slos/helpers/is_slo_feature_enabled'; + +export function SloEditPage() { + const { http } = useKibana().services; + const { ObservabilityPageTemplate, config } = usePluginContext(); + + const { sloId } = useParams<{ sloId: string | undefined }>(); + + useBreadcrumbs([ + { + href: http.basePath.prepend(paths.observability.slos), + text: i18n.translate('xpack.observability.breadcrumbs.sloEditLinkText', { + defaultMessage: 'SLOs', + }), + }, + ]); + + const { slo, loading } = useFetchSloDetails(sloId); + + if (!isSloFeatureEnabled(config)) { + return ; + } + + if (loading) { + return null; + } + + return ( + + + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx index 923f4e82adc1..787c077e2456 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx @@ -45,6 +45,10 @@ export function SloListItem({ slo, onDeleted, onDeleting }: SloListItemProps) { setIsActionsPopoverOpen(!isActionsPopoverOpen); }; + const handleEdit = () => { + navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id))); + }; + const handleDelete = () => { setDeleteConfirmationModalOpen(true); setIsDeleting(true); @@ -112,7 +116,12 @@ export function SloListItem({ slo, onDeleted, onDeleting }: SloListItemProps) { + + {i18n.translate('xpack.observability.slos.slo.item.actions.edit', { + defaultMessage: 'Edit', + })} + , + {i18n.translate('xpack.observability.slos.slo.item.actions.delete', { defaultMessage: 'Delete', })} diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_welcome_prompt.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_welcome_prompt.tsx index 198a0a5ee4da..995146379cf9 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_welcome_prompt.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_welcome_prompt.tsx @@ -8,11 +8,27 @@ import React from 'react'; import { EuiPageTemplate, EuiButton, EuiTitle, EuiLink, EuiImage } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; + +import { paths } from '../../../config'; +import { useKibana } from '../../../utils/kibana_react'; import illustration from './assets/illustration.svg'; export function SloListWelcomePrompt() { + const { + application: { navigateToUrl }, + http: { basePath }, + } = useKibana().services; + + const handleClickCreateSlo = () => { + navigateToUrl(basePath.prepend(paths.observability.sloCreate)); + }; + return ( - + @@ -51,7 +67,7 @@ export function SloListWelcomePrompt() { } actions={ - + {i18n.translate('xpack.observability.slos.sloList.welcomePrompt.buttonLabel', { defaultMessage: 'Create first SLO', })} diff --git a/x-pack/plugins/observability/public/pages/slos/helpers/index.ts b/x-pack/plugins/observability/public/pages/slos/helpers/index.ts deleted file mode 100644 index 566763f70d80..000000000000 --- a/x-pack/plugins/observability/public/pages/slos/helpers/index.ts +++ /dev/null @@ -1,8 +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. - */ - -export { isSloFeatureEnabled } from './is_slo_feature_enabled'; diff --git a/x-pack/plugins/observability/public/pages/slos/index.test.tsx b/x-pack/plugins/observability/public/pages/slos/index.test.tsx index 2ae024c9fda1..ec9533922b5e 100644 --- a/x-pack/plugins/observability/public/pages/slos/index.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos/index.test.tsx @@ -10,7 +10,6 @@ import { screen } from '@testing-library/react'; import { ConfigSchema } from '../../plugin'; import { Subset } from '../../typings'; -import { useKibana } from '../../utils/kibana_react'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import { render } from '../../utils/test_helper'; import { SlosPage } from '.'; @@ -22,25 +21,15 @@ jest.mock('react-router-dom', () => ({ useParams: jest.fn(), })); jest.mock('../../hooks/slo/use_fetch_slo_list'); -jest.mock('../../utils/kibana_react'); jest.mock('../../hooks/use_breadcrumbs'); -jest.mock('./components/slo_list_item', () => ({ SloListItem: () => 'mocked SloListItem' })); +const mockUseKibanaReturnValue = kibanaStartMock.startContract(); + +jest.mock('../../utils/kibana_react', () => ({ + useKibana: jest.fn(() => mockUseKibanaReturnValue), +})); const useFetchSloListMock = useFetchSloList as jest.Mock; -const useKibanaMock = useKibana as jest.Mock; -const mockKibana = () => { - useKibanaMock.mockReturnValue({ - services: { - ...kibanaStartMock.startContract(), - http: { - basePath: { - prepend: jest.fn(), - }, - }, - }, - }); -}; const config: Subset = { unsafe: { @@ -51,7 +40,6 @@ const config: Subset = { describe('SLOs Page', () => { beforeEach(() => { jest.clearAllMocks(); - mockKibana(); }); it('renders the not found page when the feature flag is not enabled', async () => { diff --git a/x-pack/plugins/observability/public/pages/slos/index.tsx b/x-pack/plugins/observability/public/pages/slos/index.tsx index a80f314ee59f..749413f1ac24 100644 --- a/x-pack/plugins/observability/public/pages/slos/index.tsx +++ b/x-pack/plugins/observability/public/pages/slos/index.tsx @@ -6,13 +6,15 @@ */ import React from 'react'; +import { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { ObservabilityAppServices } from '../../application/types'; import { paths } from '../../config'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useKibana } from '../../utils/kibana_react'; -import { isSloFeatureEnabled } from './helpers'; +import { isSloFeatureEnabled } from './helpers/is_slo_feature_enabled'; import { SLOS_BREADCRUMB_TEXT, SLOS_PAGE_TITLE } from './translations'; import { useFetchSloList } from '../../hooks/slo/use_fetch_slo_list'; import { SloList } from './components/slo_list'; @@ -20,7 +22,10 @@ import { SloListWelcomePrompt } from './components/slo_list_welcome_prompt'; import PageNotFound from '../404'; export function SlosPage() { - const { http } = useKibana().services; + const { + application: { navigateToUrl }, + http: { basePath }, + } = useKibana().services; const { ObservabilityPageTemplate, config } = usePluginContext(); const { @@ -30,11 +35,15 @@ export function SlosPage() { useBreadcrumbs([ { - href: http.basePath.prepend(paths.observability.slos), + href: basePath.prepend(paths.observability.slos), text: SLOS_BREADCRUMB_TEXT, }, ]); + const handleClickCreateSlo = () => { + navigateToUrl(basePath.prepend(paths.observability.sloCreate)); + }; + if (!isSloFeatureEnabled(config)) { return ; } @@ -51,7 +60,13 @@ export function SlosPage() { + {i18n.translate('xpack.observability.slos.sloList.pageHeader.createNewButtonLabel', { + defaultMessage: 'Create new SLO', + })} + , + ], bottomBorder: false, }} data-test-subj="slosPage" diff --git a/x-pack/plugins/observability/public/routes/index.tsx b/x-pack/plugins/observability/public/routes/index.tsx index 20cf331842a5..872f06dced9f 100644 --- a/x-pack/plugins/observability/public/routes/index.tsx +++ b/x-pack/plugins/observability/public/routes/index.tsx @@ -22,6 +22,7 @@ import { AlertDetails } from '../pages/alert_details'; import { DatePickerContextProvider } from '../context/date_picker_context'; import { SlosPage } from '../pages/slos'; import { SloDetailsPage } from '../pages/slo_details'; +import { SloEditPage } from '../pages/slo_edit'; export type RouteParams = DecodeParams; @@ -138,6 +139,20 @@ export const routes = { params: {}, exact: true, }, + '/slos/create': { + handler: () => { + return ; + }, + params: {}, + exact: true, + }, + '/slos/edit/:sloId': { + handler: () => { + return ; + }, + params: {}, + exact: true, + }, '/slos/:sloId': { handler: () => { return ; diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index 6862f485988a..78a254ce7fc0 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -33,8 +33,8 @@ import { IndicatorTypes } from '../../domain/models'; import { createObservabilityServerRoute } from '../create_observability_server_route'; const transformGenerators: Record = { - 'sli.apm.transaction_duration': new ApmTransactionDurationTransformGenerator(), - 'sli.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionDuration': new ApmTransactionDurationTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), 'sli.kql.custom': new KQLCustomTransformGenerator(), }; diff --git a/x-pack/plugins/observability/server/services/slo/find_slo.test.ts b/x-pack/plugins/observability/server/services/slo/find_slo.test.ts index 3b7d80eec3d1..e4982c36a073 100644 --- a/x-pack/plugins/observability/server/services/slo/find_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/find_slo.test.ts @@ -56,7 +56,7 @@ describe('FindSLO', () => { transactionType: 'irrelevant', 'threshold.us': 500000, }, - type: 'sli.apm.transaction_duration', + type: 'sli.apm.transactionDuration', }, objective: { target: 0.999, @@ -116,12 +116,12 @@ describe('FindSLO', () => { ); }); - it('calls the repository with the indicator_type filter criteria', async () => { + it('calls the repository with the indicatorType filter criteria', async () => { const slo = createSLO(); mockRepository.find.mockResolvedValueOnce(createPaginatedSLO(slo)); mockSLIClient.fetchCurrentSLIData.mockResolvedValueOnce(someIndicatorData(slo)); - await findSLO.execute({ indicator_types: ['sli.kql.custom'] }); + await findSLO.execute({ indicatorTypes: ['sli.kql.custom'] }); expect(mockRepository.find).toHaveBeenCalledWith( { indicatorTypes: ['sli.kql.custom'] }, @@ -135,7 +135,7 @@ describe('FindSLO', () => { mockRepository.find.mockResolvedValueOnce(createPaginatedSLO(slo)); mockSLIClient.fetchCurrentSLIData.mockResolvedValueOnce(someIndicatorData(slo)); - await findSLO.execute({ name: 'My SLO*', page: '2', per_page: '100' }); + await findSLO.execute({ name: 'My SLO*', page: '2', perPage: '100' }); expect(mockRepository.find).toHaveBeenCalledWith( { name: 'My SLO*' }, @@ -149,7 +149,7 @@ describe('FindSLO', () => { mockRepository.find.mockResolvedValueOnce(createPaginatedSLO(slo)); mockSLIClient.fetchCurrentSLIData.mockResolvedValueOnce(someIndicatorData(slo)); - await findSLO.execute({ page: '-1', per_page: '0' }); + await findSLO.execute({ page: '-1', perPage: '0' }); expect(mockRepository.find).toHaveBeenCalledWith( { name: undefined }, @@ -163,7 +163,7 @@ describe('FindSLO', () => { mockRepository.find.mockResolvedValueOnce(createPaginatedSLO(slo)); mockSLIClient.fetchCurrentSLIData.mockResolvedValueOnce(someIndicatorData(slo)); - await findSLO.execute({ sort_by: undefined }); + await findSLO.execute({ sortBy: undefined }); expect(mockRepository.find).toHaveBeenCalledWith( { name: undefined }, @@ -177,7 +177,7 @@ describe('FindSLO', () => { mockRepository.find.mockResolvedValueOnce(createPaginatedSLO(slo)); mockSLIClient.fetchCurrentSLIData.mockResolvedValueOnce(someIndicatorData(slo)); - await findSLO.execute({ sort_by: 'indicator_type' }); + await findSLO.execute({ sortBy: 'indicatorType' }); expect(mockRepository.find).toHaveBeenCalledWith( { name: undefined }, @@ -191,7 +191,7 @@ describe('FindSLO', () => { mockRepository.find.mockResolvedValueOnce(createPaginatedSLO(slo)); mockSLIClient.fetchCurrentSLIData.mockResolvedValueOnce(someIndicatorData(slo)); - await findSLO.execute({ sort_by: 'indicator_type', sort_direction: 'desc' }); + await findSLO.execute({ sortBy: 'indicatorType', sortDirection: 'desc' }); expect(mockRepository.find).toHaveBeenCalledWith( { name: undefined }, diff --git a/x-pack/plugins/observability/server/services/slo/find_slo.ts b/x-pack/plugins/observability/server/services/slo/find_slo.ts index 26d494c1ac6b..06134a2b692d 100644 --- a/x-pack/plugins/observability/server/services/slo/find_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/find_slo.ts @@ -73,7 +73,7 @@ function computeSloWithSummary( function toPagination(params: FindSLOParams): Pagination { const page = Number(params.page); - const perPage = Number(params.per_page); + const perPage = Number(params.perPage); return { page: !isNaN(page) && page >= 1 ? page : DEFAULT_PAGE, @@ -82,12 +82,12 @@ function toPagination(params: FindSLOParams): Pagination { } function toCriteria(params: FindSLOParams): Criteria { - return { name: params.name, indicatorTypes: params.indicator_types }; + return { name: params.name, indicatorTypes: params.indicatorTypes }; } function toSort(params: FindSLOParams): Sort { return { - field: params.sort_by === 'indicator_type' ? SortField.IndicatorType : SortField.Name, - direction: params.sort_direction === 'desc' ? SortDirection.Desc : SortDirection.Asc, + field: params.sortBy === 'indicatorType' ? SortField.IndicatorType : SortField.Name, + direction: params.sortDirection === 'desc' ? SortDirection.Desc : SortDirection.Asc, }; } diff --git a/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts b/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts index 886f99d3e2fb..b040254d570b 100644 --- a/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts +++ b/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts @@ -28,7 +28,7 @@ import { sevenDaysRolling } from './time_window'; export const createAPMTransactionErrorRateIndicator = ( params: Partial = {} ): Indicator => ({ - type: 'sli.apm.transaction_error_rate', + type: 'sli.apm.transactionErrorRate', params: { environment: 'irrelevant', service: 'irrelevant', @@ -42,7 +42,7 @@ export const createAPMTransactionErrorRateIndicator = ( export const createAPMTransactionDurationIndicator = ( params: Partial = {} ): Indicator => ({ - type: 'sli.apm.transaction_duration', + type: 'sli.apm.transactionDuration', params: { environment: 'irrelevant', service: 'irrelevant', diff --git a/x-pack/plugins/observability/server/services/slo/get_slo.test.ts b/x-pack/plugins/observability/server/services/slo/get_slo.test.ts index daa2003dd6c1..b0752400c2cd 100644 --- a/x-pack/plugins/observability/server/services/slo/get_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/get_slo.test.ts @@ -51,7 +51,7 @@ describe('GetSLO', () => { transactionType: 'irrelevant', goodStatusCodes: ['2xx', '3xx', '4xx'], }, - type: 'sli.apm.transaction_error_rate', + type: 'sli.apm.transactionErrorRate', }, objective: { target: 0.999, diff --git a/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts index 1123d8adea71..f93590f82d8a 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts @@ -197,7 +197,7 @@ describe('KibanaSavedObjectsSLORepository', () => { soClientMock.find.mockResolvedValueOnce(aFindResponse(SOME_SLO)); const result = await repository.find( - { indicatorTypes: ['sli.kql.custom', 'sli.apm.transaction_duration'] }, + { indicatorTypes: ['sli.kql.custom', 'sli.apm.transactionDuration'] }, DEFAULT_SORTING, DEFAULT_PAGINATION ); @@ -212,7 +212,7 @@ describe('KibanaSavedObjectsSLORepository', () => { type: SO_SLO_TYPE, page: 1, perPage: 25, - filter: `(slo.attributes.indicator.type: sli.kql.custom or slo.attributes.indicator.type: sli.apm.transaction_duration)`, + filter: `(slo.attributes.indicator.type: sli.kql.custom or slo.attributes.indicator.type: sli.apm.transactionDuration)`, sortField: 'name', sortOrder: 'asc', }); @@ -224,7 +224,7 @@ describe('KibanaSavedObjectsSLORepository', () => { soClientMock.find.mockResolvedValueOnce(aFindResponse(SOME_SLO)); const result = await repository.find( - { name: 'latency', indicatorTypes: ['sli.kql.custom', 'sli.apm.transaction_duration'] }, + { name: 'latency', indicatorTypes: ['sli.kql.custom', 'sli.apm.transactionDuration'] }, DEFAULT_SORTING, DEFAULT_PAGINATION ); @@ -239,7 +239,7 @@ describe('KibanaSavedObjectsSLORepository', () => { type: SO_SLO_TYPE, page: 1, perPage: 25, - filter: `(slo.attributes.name: *latency*) and (slo.attributes.indicator.type: sli.kql.custom or slo.attributes.indicator.type: sli.apm.transaction_duration)`, + filter: `(slo.attributes.name: *latency*) and (slo.attributes.indicator.type: sli.kql.custom or slo.attributes.indicator.type: sli.apm.transactionDuration)`, sortField: 'name', sortOrder: 'asc', }); diff --git a/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts b/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts index 0b97ae3aa6dc..0b0074be5b26 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_manager.test.ts @@ -41,19 +41,19 @@ describe('TransformManager', () => { it('throws when no generator exists for the slo indicator type', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transaction_duration': new DummyTransformGenerator(), + 'sli.apm.transactionDuration': new DummyTransformGenerator(), }; const service = new DefaultTransformManager(generators, esClientMock, loggerMock); await expect( service.install(createSLO({ indicator: createAPMTransactionErrorRateIndicator() })) - ).rejects.toThrowError('Unsupported SLI type: sli.apm.transaction_error_rate'); + ).rejects.toThrowError('Unsupported SLI type: sli.apm.transactionErrorRate'); }); it('throws when transform generator fails', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transaction_duration': new FailTransformGenerator(), + 'sli.apm.transactionDuration': new FailTransformGenerator(), }; const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); @@ -68,7 +68,7 @@ describe('TransformManager', () => { it('installs the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), }; const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); const slo = createSLO({ indicator: createAPMTransactionErrorRateIndicator() }); @@ -84,7 +84,7 @@ describe('TransformManager', () => { it('starts the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), }; const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); @@ -98,7 +98,7 @@ describe('TransformManager', () => { it('stops the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), }; const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); @@ -112,7 +112,7 @@ describe('TransformManager', () => { it('uninstalls the transform', async () => { // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), }; const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); @@ -127,7 +127,7 @@ describe('TransformManager', () => { ); // @ts-ignore defining only a subset of the possible SLI const generators: Record = { - 'sli.apm.transaction_error_rate': new ApmTransactionErrorRateTransformGenerator(), + 'sli.apm.transactionErrorRate': new ApmTransactionErrorRateTransformGenerator(), }; const transformManager = new DefaultTransformManager(generators, esClientMock, loggerMock); diff --git a/x-pack/plugins/observability/tsconfig.json b/x-pack/plugins/observability/tsconfig.json index 78209f44aa17..dbb01d971f9a 100644 --- a/x-pack/plugins/observability/tsconfig.json +++ b/x-pack/plugins/observability/tsconfig.json @@ -67,6 +67,7 @@ "@kbn/share-plugin", "@kbn/core-notifications-browser", "@kbn/slo-schema", + "@kbn/core-http-server-internal", ], "exclude": [ "target/**/*", From ed840b5351c0460f64999b4d7a0a26b3614de641 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 3 Jan 2023 19:08:52 +0000 Subject: [PATCH 21/30] skip flaky journey (#148221) --- .../journeys/ecommerce_dashboard_saved_search_only.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/performance/journeys/ecommerce_dashboard_saved_search_only.ts b/x-pack/performance/journeys/ecommerce_dashboard_saved_search_only.ts index c63f239c5a49..fc32c62ce6ba 100644 --- a/x-pack/performance/journeys/ecommerce_dashboard_saved_search_only.ts +++ b/x-pack/performance/journeys/ecommerce_dashboard_saved_search_only.ts @@ -10,6 +10,8 @@ import { subj } from '@kbn/test-subj-selector'; import { waitForVisualizations } from '../utils'; export const journey = new Journey({ + // FAILING: https://github.com/elastic/kibana/issues/148221 + skipped: true, esArchives: ['x-pack/performance/es_archives/sample_data_ecommerce'], kbnArchives: ['x-pack/performance/kbn_archives/ecommerce_saved_search_only_dashboard'], }) From 3abf705b10926d3c6221504dd5575b97d15c9a31 Mon Sep 17 00:00:00 2001 From: christineweng <18648970+christineweng@users.noreply.github.com> Date: Tue, 3 Jan 2023 13:21:48 -0600 Subject: [PATCH 22/30] [Security Solution][Bug] Add privilege check in open timeline (#147964) ## Summary This PR contains fixe for https://github.com/elastic/kibana/issues/147544. On Timelines page, a Kibana read-only user was able to see and click on options to create and duplicate timelines. This PR fixes this bug by checking user privilege (have crud access) before showing timeline actions. ## After: User with read only access to kibana security solutions can: 1) select timelines 2) export timelines 3) export timelines in bulk User with crud access to kibana security solutions can: 1) select timelines 2) have the options to modify timelines as before 3) bulk actions include delete timelines and export timelines 4) see and click on 'import', ' Create new timeline', 'Create new timeline template' buttons ### User with read access but not crud access - Have access to export ('Export selected'), cannot see 'Create new timeline' buttons ![image](https://user-images.githubusercontent.com/18648970/209210913-0554bc4c-5c8e-45ae-8e27-54a7e33e3f8e.png) - 'Export selected' in bulk actions ![image](https://user-images.githubusercontent.com/18648970/209210992-f102d8d4-479f-4d0a-84c2-125cc754c5ce.png) ![image](https://user-images.githubusercontent.com/18648970/209021998-fbe0b63d-8dfd-4098-9774-7423899a45e1.png) ### User with full access ![image](https://user-images.githubusercontent.com/18648970/209209755-b5e5ce2b-0af9-42c6-b1cc-64a2675bf19d.png) - 'Export selected' and 'Delete selected' available in bulk actions dropdown ![image](https://user-images.githubusercontent.com/18648970/210408773-0fc5b100-0f57-4526-9c8f-0aba1f1d0e76.png) ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../edit_timeline_batch_actions.tsx | 51 ++++++----- .../components/open_timeline/index.test.tsx | 7 ++ .../open_timeline/open_timeline.test.tsx | 86 +++++++++++++++++++ .../open_timeline/open_timeline.tsx | 56 +++++++----- .../timelines_table/actions_columns.tsx | 5 +- .../open_timeline/timelines_table/index.tsx | 9 +- .../public/timelines/pages/timelines_page.tsx | 44 +++++----- 7 files changed, 188 insertions(+), 70 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx index cec4cd42775c..292755be1aa3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/edit_timeline_batch_actions.tsx @@ -71,6 +71,33 @@ export const useEditTimelineBatchActions = ({ const getBatchItemsPopoverContent = useCallback( (closePopover: () => void) => { const disabled = selectedItems == null || selectedItems.length === 0; + const items = []; + if (selectedItems) { + items.push( + + {i18n.EXPORT_SELECTED} + + ); + } + if (deleteTimelines) { + items.push( + + {i18n.DELETE_SELECTED} + + ); + } return ( <> - - - {i18n.EXPORT_SELECTED} - , - - {i18n.DELETE_SELECTED} - , - ]} - /> + ); }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx index 32ab4d7d50ad..3ebfd7d93e31 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx @@ -29,6 +29,7 @@ import { TimelineTabsStyle } from './types'; import type { UseTimelineTypesArgs, UseTimelineTypesResult } from './use_timeline_types'; import { useTimelineTypes } from './use_timeline_types'; import { deleteTimelinesByIds } from '../../containers/api'; +import { useUserPrivileges } from '../../../common/components/user_privileges'; jest.mock('react-router-dom', () => { const originalModule = jest.requireActual('react-router-dom'); @@ -86,6 +87,9 @@ jest.mock('../../containers/api', () => ({ deleteTimelinesByIds: jest.fn(), })); +jest.mock('../../../common/components/user_privileges'); +const useUserPrivilegesMock = useUserPrivileges as jest.Mock; + describe('StatefulOpenTimeline', () => { const title = 'All Timelines / Open Timelines'; let mockHistory: History[]; @@ -96,6 +100,9 @@ describe('StatefulOpenTimeline', () => { tabName: TimelineType.default, pageName: SecurityPageName.timelines, }); + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); mockHistory = []; (useHistory as jest.Mock).mockReturnValue(mockHistory); (useGetAllTimeline as unknown as jest.Mock).mockReturnValue({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx index fe673db13904..55ebfaa3473f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx @@ -20,6 +20,7 @@ import { OpenTimeline } from './open_timeline'; import { DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD } from './constants'; import { TimelineType, TimelineStatus } from '../../../../common/types/timeline'; import { getMockTheme } from '../../../common/lib/kibana/kibana_react.mock'; +import { useUserPrivileges } from '../../../common/components/user_privileges'; jest.mock('../../../common/lib/kibana'); @@ -42,6 +43,9 @@ const mockTheme = getMockTheme({ }, }); +jest.mock('../../../common/components/user_privileges'); +const useUserPrivilegesMock = useUserPrivileges as jest.Mock; + describe('OpenTimeline', () => { const title = 'All Timelines / Open Timelines'; @@ -102,6 +106,9 @@ describe('OpenTimeline', () => { }); test('it shows the delete action columns when onDeleteSelected and deleteTimelines are specified', () => { + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); const defaultProps = getDefaultTestProps(mockResults); const wrapper = mountWithIntl( @@ -177,6 +184,29 @@ describe('OpenTimeline', () => { expect(props.actionTimelineToShow).not.toContain('delete'); }); + test('it does NOT show the delete action when user has read only access', () => { + const defaultProps = { + ...getDefaultTestProps(mockResults), + onDeleteSelected: undefined, + deleteTimelines: undefined, + }; + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: false, read: true }, + }); + const wrapper = mountWithIntl( + + + + ); + + const props = wrapper + .find('[data-test-subj="timelines-table"]') + .first() + .props() as TimelinesTableProps; + + expect(props.actionTimelineToShow).not.toContain('delete'); + }); + test('it renders an empty string when the query is an empty string', () => { const defaultProps = { ...getDefaultTestProps(mockResults), @@ -324,6 +354,9 @@ describe('OpenTimeline', () => { }); test('it should disable delete timeline if no timeline is selected', async () => { + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); const defaultProps = { ...getDefaultTestProps(mockResults), timelineStatus: null, @@ -372,6 +405,9 @@ describe('OpenTimeline', () => { }); test('it should enable delete timeline if a timeline is selected', async () => { + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); const defaultProps = { ...getDefaultTestProps(mockResults), timelineStatus: null, @@ -396,6 +432,9 @@ describe('OpenTimeline', () => { }); test("it should render a selectable timeline table if timelineStatus is active (selecting custom templates' tab)", () => { + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); const defaultProps = { ...getDefaultTestProps(mockResults), timelineStatus: TimelineStatus.active, @@ -412,6 +451,9 @@ describe('OpenTimeline', () => { }); test('it should include createRule in timeline actions if onCreateRule is passed', () => { + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); const defaultProps = { ...getDefaultTestProps(mockResults), timelineStatus: TimelineStatus.active, @@ -427,6 +469,25 @@ describe('OpenTimeline', () => { ).toEqual(['createFrom', 'duplicate', 'createRule', 'export', 'selectable', 'delete']); }); + test('it should NOT include createFrom, duplicate, createRule, delete in timeline actions when user has read only access', () => { + const defaultProps = { + ...getDefaultTestProps(mockResults), + timelineStatus: TimelineStatus.active, + }; + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: false, read: true }, + }); + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="timelines-table"]').first().prop('actionTimelineToShow') + ).toEqual(['export', 'selectable']); + }); + test("it should render selected count if timelineStatus is active (selecting custom templates' tab)", () => { const defaultProps = { ...getDefaultTestProps(mockResults), @@ -456,6 +517,9 @@ describe('OpenTimeline', () => { }); test("it should not render a selectable timeline table if timelineStatus is immutable (selecting Elastic templates' tab)", () => { + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); const defaultProps = { ...getDefaultTestProps(mockResults), timelineStatus: TimelineStatus.immutable, @@ -500,6 +564,9 @@ describe('OpenTimeline', () => { }); test("it should render a selectable timeline table if timelineStatus is null (no template timelines' tab selected)", () => { + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, + }); const defaultProps = { ...getDefaultTestProps(mockResults), timelineStatus: null, @@ -515,6 +582,25 @@ describe('OpenTimeline', () => { ).toEqual(['createFrom', 'duplicate', 'export', 'selectable', 'delete']); }); + test("it should render a selectable timeline table if timelineStatus is null (no template timelines' tab selected) and user has read only access", () => { + const defaultProps = { + ...getDefaultTestProps(mockResults), + timelineStatus: null, + }; + useUserPrivilegesMock.mockReturnValue({ + kibanaSecuritySolutionsPrivileges: { crud: false, read: true }, + }); + const wrapper = mountWithIntl( + + + + ); + + expect( + wrapper.find('[data-test-subj="timelines-table"]').first().prop('actionTimelineToShow') + ).toEqual(['export', 'selectable']); + }); + test("it should render selected count if timelineStatus is null (no template timelines' tab selected)", () => { const defaultProps = { ...getDefaultTestProps(mockResults), diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx index 9d3ecf049280..d58874668644 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx @@ -21,7 +21,7 @@ import { } from '../../../common/components/utility_bar'; import { importTimelines } from '../../containers/api'; - +import { useUserPrivileges } from '../../../common/components/user_privileges'; import { useEditTimelineBatchActions } from './edit_timeline_batch_actions'; import { useEditTimelineActions } from './edit_timeline_actions'; import { EditTimelineActions } from './export_timeline'; @@ -78,8 +78,9 @@ export const OpenTimeline = React.memo( onCompleteEditTimelineAction, } = useEditTimelineActions(); + const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges(); const { getBatchItemsPopoverContent } = useEditTimelineBatchActions({ - deleteTimelines, + deleteTimelines: kibanaSecuritySolutionsPrivileges.crud ? deleteTimelines : undefined, selectedItems, tableRef, timelineType, @@ -149,28 +150,41 @@ export const OpenTimeline = React.memo( }, [setImportDataModalToggle, refetch]); const actionTimelineToShow = useMemo(() => { - const createRule: ActionTimelineToShow[] = ['createRule']; - const timelineActions: ActionTimelineToShow[] = [ - 'createFrom', - 'duplicate', - ...(onCreateRule != null ? createRule : []), - ]; + if (kibanaSecuritySolutionsPrivileges.crud) { + const createRule: ActionTimelineToShow[] = ['createRule']; + const timelineActions: ActionTimelineToShow[] = [ + 'createFrom', + 'duplicate', + ...(onCreateRule != null ? createRule : []), + ]; - if (timelineStatus !== TimelineStatus.immutable) { - timelineActions.push('export'); - timelineActions.push('selectable'); - } + if (timelineStatus !== TimelineStatus.immutable) { + timelineActions.push('export'); + timelineActions.push('selectable'); + } - if ( - onDeleteSelected != null && - deleteTimelines != null && - timelineStatus !== TimelineStatus.immutable - ) { - timelineActions.push('delete'); - } + if ( + onDeleteSelected != null && + deleteTimelines != null && + timelineStatus !== TimelineStatus.immutable + ) { + timelineActions.push('delete'); + } - return timelineActions; - }, [onCreateRule, timelineStatus, onDeleteSelected, deleteTimelines]); + return timelineActions; + } + // user with read access should only see export + if (timelineStatus !== TimelineStatus.immutable) { + return ['export', 'selectable']; + } + return []; + }, [ + onCreateRule, + timelineStatus, + onDeleteSelected, + deleteTimelines, + kibanaSecuritySolutionsPrivileges, + ]); const SearchRowContent = useMemo(() => <>{templateTimelineFilter}, [templateTimelineFilter]); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx index 29d3c560cc74..75c52a34c3e7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx @@ -27,6 +27,7 @@ export const getActionsColumns = ({ onOpenDeleteTimelineModal, onOpenTimeline, onCreateRule, + hasCrudAccess, }: { actionTimelineToShow: ActionTimelineToShow[]; deleteTimelines?: DeleteTimelines; @@ -34,6 +35,7 @@ export const getActionsColumns = ({ onOpenDeleteTimelineModal?: OnOpenDeleteTimelineModal; onOpenTimeline: OnOpenTimeline; onCreateRule?: OnCreateRuleFromTimeline; + hasCrudAccess: boolean; }): [TimelineActionsOverflowColumns] => { const createTimelineFromTemplate = { name: i18n.CREATE_TIMELINE_FROM_TEMPLATE, @@ -149,10 +151,9 @@ export const getActionsColumns = ({ 'data-test-subj': 'create-rule-from-timeline', available: () => actionTimelineToShow.includes('createRule') && onCreateRule != null, }; - return [ { - width: '80px', + width: hasCrudAccess ? '80px' : '150px', actions: [ createTimelineFromTemplate, createTemplateFromTimeline, diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx index 9dfc3d1670b0..9634a31714b1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/index.tsx @@ -28,7 +28,7 @@ import { getExtendedColumns } from './extended_columns'; import { getIconHeaderColumns } from './icon_header_columns'; import type { TimelineTypeLiteralWithNull } from '../../../../../common/types/timeline'; import { TimelineStatus, TimelineType } from '../../../../../common/types/timeline'; - +import { useUserPrivileges } from '../../../../common/components/user_privileges'; // there are a number of type mismatches across this file const EuiBasicTable: any = _EuiBasicTable; // eslint-disable-line @typescript-eslint/no-explicit-any @@ -61,6 +61,7 @@ export const getTimelinesTableColumns = ({ onToggleShowNotes, showExtendedColumns, timelineType, + hasCrudAccess, }: { actionTimelineToShow: ActionTimelineToShow[]; deleteTimelines?: DeleteTimelines; @@ -73,6 +74,7 @@ export const getTimelinesTableColumns = ({ onToggleShowNotes: OnToggleShowNotes; showExtendedColumns: boolean; timelineType: TimelineTypeLiteralWithNull; + hasCrudAccess: boolean; }) => { return [ ...getCommonColumns({ @@ -91,6 +93,7 @@ export const getTimelinesTableColumns = ({ enableExportTimelineDownloader, onOpenDeleteTimelineModal, onOpenTimeline, + hasCrudAccess, }) : []), ]; @@ -176,7 +179,7 @@ export const TimelinesTable = React.memo( onSelectionChange, }; const basicTableProps = tableRef != null ? { ref: tableRef } : {}; - + const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges(); const columns = useMemo( () => getTimelinesTableColumns({ @@ -191,6 +194,7 @@ export const TimelinesTable = React.memo( onToggleShowNotes, showExtendedColumns, timelineType, + hasCrudAccess: kibanaSecuritySolutionsPrivileges.crud, }), [ actionTimelineToShow, @@ -204,6 +208,7 @@ export const TimelinesTable = React.memo( onToggleShowNotes, showExtendedColumns, timelineType, + kibanaSecuritySolutionsPrivileges, ] ); diff --git a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx index bf402eb56c29..2b5a9baaa9bc 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx @@ -47,9 +47,9 @@ export const TimelinesPageComponent: React.FC = () => { <> - - - {capabilitiesCanUserCRUD && ( + {capabilitiesCanUserCRUD && ( + + { > {i18n.ALL_TIMELINES_IMPORT_TIMELINE_TITLE} - )} - - {tabName === TimelineType.default ? ( - - - ) : ( - - - - )} - + {tabName === TimelineType.default ? ( + + + + ) : ( + + + + )} + + )} From 72d2f7528735cc2e02f07b29c3a3388790550f2a Mon Sep 17 00:00:00 2001 From: Ashokaditya <1849116+ashokaditya@users.noreply.github.com> Date: Tue, 3 Jan 2023 20:35:25 +0100 Subject: [PATCH 23/30] [Fleet][RBAC v2] Update `agent_status` route to use `calculateRouteAuthz` (#147696) ## Summary Follow up PR to update `api/fleet/agent_status` route. refs elastic/kibana/pull/145361 refs elastic/security-team/issues/5539 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../fleet/server/routes/agent/index.ts | 14 +++++++--- .../services/security/route_required_authz.ts | 20 ++++++++++++- .../apis/agents/status.ts | 28 +++++++++++++++++++ .../fleet_api_integration/apis/test_users.ts | 24 ++++++++++++++++ 4 files changed, 81 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/fleet/server/routes/agent/index.ts b/x-pack/plugins/fleet/server/routes/agent/index.ts index 886d0228f8bb..d7eb3657cbfd 100644 --- a/x-pack/plugins/fleet/server/routes/agent/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent/index.ts @@ -5,7 +5,9 @@ * 2.0. */ -import type { FleetAuthzRouter } from '../../services/security'; +import type { FleetAuthz } from '../../../common'; + +import { getRouteRequiredAuthz, type FleetAuthzRouter } from '../../services/security'; import { AGENT_API_ROUTES } from '../../constants'; import { @@ -35,6 +37,8 @@ import type { FleetConfigType } from '../..'; import { PostBulkUpdateAgentTagsRequestSchema } from '../../types/rest_spec/agent'; +import { calculateRouteAuthz } from '../../services/security/security'; + import { getAgentsHandler, getAgentTagsHandler, @@ -234,9 +238,11 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT { path: AGENT_API_ROUTES.STATUS_PATTERN, validate: GetAgentStatusRequestSchema, - fleetAuthz: { - fleet: { all: true }, - }, + fleetAuthz: (fleetAuthz: FleetAuthz): boolean => + calculateRouteAuthz( + fleetAuthz, + getRouteRequiredAuthz('get', AGENT_API_ROUTES.STATUS_PATTERN) + ).granted, }, getAgentStatusForAgentPolicyHandler ); diff --git a/x-pack/plugins/fleet/server/services/security/route_required_authz.ts b/x-pack/plugins/fleet/server/services/security/route_required_authz.ts index db0ea31eff7a..107279f5f33e 100644 --- a/x-pack/plugins/fleet/server/services/security/route_required_authz.ts +++ b/x-pack/plugins/fleet/server/services/security/route_required_authz.ts @@ -9,7 +9,7 @@ import { deepFreeze } from '@kbn/std'; import type { RouteMethod } from '@kbn/core-http-server'; -import { PACKAGE_POLICY_API_ROUTES } from '../../../common'; +import { PACKAGE_POLICY_API_ROUTES, AGENT_API_ROUTES } from '../../../common'; import type { FleetRouteRequiredAuthz } from './types'; @@ -126,6 +126,24 @@ const ROUTE_AUTHZ_REQUIREMENTS = deepFreeze { before(async () => { @@ -197,5 +199,31 @@ export default function ({ getService }: FtrProviderContext) { it('should work with deprecated api', async () => { await supertest.get(`/api/fleet/agent-status`).expect(200); }); + + it('should work with adequate package privileges', async () => { + await superTestWithoutAuth + .get(`/api/fleet/agent_status`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_fleet_all_integr_read_policy.username, + testUsers.endpoint_fleet_all_integr_read_policy.password + ) + .expect(200); + }); + + it('should not work without adequate package privileges', async () => { + await superTestWithoutAuth + .get(`/api/fleet/agent_status`) + .set('kbn-xsrf', 'xxxx') + .auth( + testUsers.endpoint_fleet_read_integr_none.username, + testUsers.endpoint_fleet_read_integr_none.password + ) + .expect(403, { + error: 'Forbidden', + message: 'Forbidden', + statusCode: 403, + }); + }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/test_users.ts b/x-pack/test/fleet_api_integration/apis/test_users.ts index 2b55facefbc7..a202283cca9c 100644 --- a/x-pack/test/fleet_api_integration/apis/test_users.ts +++ b/x-pack/test/fleet_api_integration/apis/test_users.ts @@ -104,6 +104,30 @@ export const testUsers: { username: 'endpoint_integr_write_policy', password: 'changeme', }, + // agent status API + endpoint_fleet_all_integr_read_policy: { + permissions: { + feature: { + fleet: ['all'], + siem: ['minimal_all', 'policy_management_read'], + }, + spaces: ['*'], + }, + username: 'endpoint_fleet_all_integr_read_policy', + password: 'changeme', + }, + // no access to integrations or policies + endpoint_fleet_read_integr_none: { + permissions: { + feature: { + fleet: ['read'], + siem: ['minimal_all'], + }, + spaces: ['*'], + }, + username: 'endpoint_fleet_read_integr_none', + password: 'changeme', + }, }; export const setupTestUsers = async (security: SecurityService) => { From 5cf614b24618f72b5bf462ed03718777d538c5eb Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 3 Jan 2023 22:35:55 +0300 Subject: [PATCH 24/30] [Step 2][ESQL] Autocomplete part (#148307) ## Summary Autocomplete support for ESQL lang. Initially target branch for that PR was [elastic:feature-esql](https://github.com/elastic/kibana/tree/feature-esql) but then we decided to merge it separately. This PR is as copy of #148006 ## Notes: Important: please do `yarn kbn clean & yarn kbn bootstrap` before testing. ## How to update syntax. `antlr` syntax was copied from `ES` and was slightly modified. In case if you need to update it please follow next steps: - modify `esql_parser.g4 `and/or `esql_lexer.g4` files - go to `kbn-monaco` package and execute `bazel clean & npm run build:antlr4ts:painless` - go to /painless_parser.ts file and revert the following change: image - do `yarn kbn bootstrap` --- packages/kbn-monaco/index.ts | 3 +- .../kbn-monaco/src/common/error_listener.ts | 5 +- .../kbn-monaco/src/esql/antlr/esql_lexer.g4 | 51 +- .../src/esql/antlr/esql_lexer.interp | 75 +- .../src/esql/antlr/esql_lexer.tokens | 120 +- .../kbn-monaco/src/esql/antlr/esql_lexer.ts | 641 ++++--- .../kbn-monaco/src/esql/antlr/esql_parser.g4 | 60 +- .../src/esql/antlr/esql_parser.interp | 53 +- .../src/esql/antlr/esql_parser.tokens | 120 +- .../kbn-monaco/src/esql/antlr/esql_parser.ts | 1507 +++++++---------- .../src/esql/antlr/esql_parser_listener.ts | 218 +-- packages/kbn-monaco/src/esql/index.ts | 2 +- packages/kbn-monaco/src/esql/language.ts | 9 +- .../kbn-monaco/src/esql/lib/antlr_facade.ts | 13 +- .../comparison_commands.ts | 88 + .../dynamic_commands.ts | 60 + .../functions_commands.ts | 87 + .../autocomplete_definitions/index.ts | 36 + .../operators_commands.ts | 89 + .../ordering_commands.ts | 54 + .../processing_commands.ts | 99 ++ .../source_commands.ts | 31 + .../autocomplete_definitions/utils.ts | 39 + .../autocomplete_listener.test.ts | 109 ++ .../lib/autocomplete/autocomplete_listener.ts | 316 ++++ .../src/esql/lib/autocomplete/dymanic_item.ts | 18 + .../src/esql/lib/autocomplete/types.ts | 32 + .../lib/monaco/esql_completion_provider.ts | 111 ++ .../src/esql/lib/monaco/esql_theme.ts | 77 +- .../kbn-monaco/src/esql/worker/esql_worker.ts | 39 +- packages/kbn-monaco/src/types.ts | 2 +- 31 files changed, 2422 insertions(+), 1742 deletions(-) create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/comparison_commands.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/functions_commands.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/index.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/operators_commands.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/ordering_commands.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/processing_commands.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/source_commands.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/utils.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.test.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/dymanic_item.ts create mode 100644 packages/kbn-monaco/src/esql/lib/autocomplete/types.ts create mode 100644 packages/kbn-monaco/src/esql/lib/monaco/esql_completion_provider.ts diff --git a/packages/kbn-monaco/index.ts b/packages/kbn-monaco/index.ts index 057ea84d0709..d7ff4d4abc94 100644 --- a/packages/kbn-monaco/index.ts +++ b/packages/kbn-monaco/index.ts @@ -11,7 +11,8 @@ import './src/register_globals'; export { monaco } from './src/monaco_imports'; export { XJsonLang } from './src/xjson'; export { SQLLang } from './src/sql'; -export { ESQL_LANG_ID, ESQL_THEME_ID } from './src/esql'; +export { ESQL_LANG_ID, ESQL_THEME_ID, ESQLLang } from './src/esql'; +export type { ESQLCustomAutocompleteCallbacks } from './src/esql'; export * from './src/painless'; /* eslint-disable-next-line @kbn/eslint/module_migration */ diff --git a/packages/kbn-monaco/src/common/error_listener.ts b/packages/kbn-monaco/src/common/error_listener.ts index bc7b56a01646..b072b132b26a 100644 --- a/packages/kbn-monaco/src/common/error_listener.ts +++ b/packages/kbn-monaco/src/common/error_listener.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { ANTLRErrorListener, RecognitionException, Recognizer } from 'antlr4ts'; +import { ANTLRErrorListener, Recognizer } from 'antlr4ts'; import type { EditorError } from '../types'; export class ANTLREErrorListener implements ANTLRErrorListener { @@ -17,8 +17,7 @@ export class ANTLREErrorListener implements ANTLRErrorListener { offendingSymbol: any, line: number, column: number, - message: string, - e: RecognitionException | undefined + message: string ): void { let endColumn = column + 1; diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 b/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 index dd608ac7771e..49d73416712a 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 @@ -29,7 +29,6 @@ WS : [ \r\n\t]+ -> channel(HIDDEN) ; - mode EXPRESSION; PIPE : '|' -> popMode; @@ -73,30 +72,30 @@ DECIMAL_LITERAL BY : 'by'; AND : 'and'; -ASC : 'asc'; ASSIGN : '='; COMMA : ','; -DESC : 'desc'; DOT : '.'; -FALSE : 'false'; -FIRST : 'first'; -LAST : 'last'; LP : '('; OPENING_BRACKET : '[' -> pushMode(DEFAULT_MODE); CLOSING_BRACKET : ']' -> popMode, popMode; // pop twice, once to clear mode of current cmd and once to exit DEFAULT_MODE NOT : 'not'; NULL : 'null'; -NULLS : 'nulls'; OR : 'or'; RP : ')'; -TRUE : 'true'; -EQ : '=='; -NEQ : '!='; -LT : '<'; -LTE : '<='; -GT : '>'; -GTE : '>='; +BOOLEAN_VALUE + : 'true' + | 'false' + ; + +COMPARISON_OPERATOR + : '==' + |'!=' + | '<' + | '<=' + | '>' + | '>=' + ; PLUS : '+'; MINUS : '-'; @@ -104,12 +103,24 @@ ASTERISK : '*'; SLASH : '/'; PERCENT : '%'; -ROUND_FUNCTION_MATH : 'round'; -AVG_FUNCTION_MATH : 'avg'; -SUM_FUNCTION_MATH : 'sum'; -MIN_FUNCTION_MATH : 'min'; -MAX_FUNCTION_MATH : 'max'; +ORDERING + : 'asc' + | 'desc' + ; + +NULLS_ORDERING: 'nulls'; +NULLS_ORDERING_DIRECTION + : 'first' + | 'last' + ; +UNARY_FUNCTION + : 'round' + | 'avg' + | 'min' + | 'max' + | 'sum' + ; UNQUOTED_IDENTIFIER : (LETTER | '_') (LETTER | DIGIT | '_')* @@ -163,5 +174,3 @@ SRC_MULTILINE_COMMENT SRC_WS : WS -> channel(HIDDEN) ; - -UNKNOWN_CMD : ~[ \r\n\t[\]/]+ -> pushMode(EXPRESSION); diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp b/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp index 48aad053f07e..a7fee84591c3 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp @@ -18,39 +18,26 @@ null null 'by' 'and' -'asc' null null -'desc' '.' -'false' -'first' -'last' '(' '[' ']' 'not' 'null' -'nulls' 'or' ')' -'true' -'==' -'!=' -'<' -'<=' -'>' -'>=' +null +null '+' '-' '*' '/' '%' -'round' -'avg' -'sum' -'min' -'max' +null +'nulls' +null null null null @@ -83,39 +70,27 @@ INTEGER_LITERAL DECIMAL_LITERAL BY AND -ASC ASSIGN COMMA -DESC DOT -FALSE -FIRST -LAST LP OPENING_BRACKET CLOSING_BRACKET NOT NULL -NULLS OR RP -TRUE -EQ -NEQ -LT -LTE -GT -GTE +BOOLEAN_VALUE +COMPARISON_OPERATOR PLUS MINUS ASTERISK SLASH PERCENT -ROUND_FUNCTION_MATH -AVG_FUNCTION_MATH -SUM_FUNCTION_MATH -MIN_FUNCTION_MATH -MAX_FUNCTION_MATH +ORDERING +NULLS_ORDERING +NULLS_ORDERING_DIRECTION +UNARY_FUNCTION UNQUOTED_IDENTIFIER QUOTED_IDENTIFIER EXPR_LINE_COMMENT @@ -126,7 +101,6 @@ SRC_QUOTED_IDENTIFIER SRC_LINE_COMMENT SRC_MULTILINE_COMMENT SRC_WS -UNKNOWN_CMD rule names: EVAL @@ -152,39 +126,27 @@ INTEGER_LITERAL DECIMAL_LITERAL BY AND -ASC ASSIGN COMMA -DESC DOT -FALSE -FIRST -LAST LP OPENING_BRACKET CLOSING_BRACKET NOT NULL -NULLS OR RP -TRUE -EQ -NEQ -LT -LTE -GT -GTE +BOOLEAN_VALUE +COMPARISON_OPERATOR PLUS MINUS ASTERISK SLASH PERCENT -ROUND_FUNCTION_MATH -AVG_FUNCTION_MATH -SUM_FUNCTION_MATH -MIN_FUNCTION_MATH -MAX_FUNCTION_MATH +ORDERING +NULLS_ORDERING +NULLS_ORDERING_DIRECTION +UNARY_FUNCTION UNQUOTED_IDENTIFIER QUOTED_IDENTIFIER EXPR_LINE_COMMENT @@ -200,7 +162,6 @@ SRC_QUOTED_IDENTIFIER SRC_LINE_COMMENT SRC_MULTILINE_COMMENT SRC_WS -UNKNOWN_CMD channel names: DEFAULT_TOKEN_CHANNEL @@ -212,4 +173,4 @@ EXPRESSION SOURCE_IDENTIFIERS atn: -[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 2, 64, 573, 8, 1, 8, 1, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 7, 11, 225, 10, 11, 12, 11, 14, 11, 228, 11, 11, 3, 11, 5, 11, 231, 10, 11, 3, 11, 5, 11, 234, 10, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 7, 12, 243, 10, 12, 12, 12, 14, 12, 246, 11, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 6, 13, 254, 10, 13, 13, 13, 14, 13, 255, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 5, 19, 275, 10, 19, 3, 19, 6, 19, 278, 10, 19, 13, 19, 14, 19, 279, 3, 20, 3, 20, 3, 20, 7, 20, 285, 10, 20, 12, 20, 14, 20, 288, 11, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 7, 20, 296, 10, 20, 12, 20, 14, 20, 299, 11, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 5, 20, 306, 10, 20, 3, 20, 5, 20, 309, 10, 20, 5, 20, 311, 10, 20, 3, 21, 6, 21, 314, 10, 21, 13, 21, 14, 21, 315, 3, 22, 6, 22, 319, 10, 22, 13, 22, 14, 22, 320, 3, 22, 3, 22, 7, 22, 325, 10, 22, 12, 22, 14, 22, 328, 11, 22, 3, 22, 3, 22, 6, 22, 332, 10, 22, 13, 22, 14, 22, 333, 3, 22, 6, 22, 337, 10, 22, 13, 22, 14, 22, 338, 3, 22, 3, 22, 7, 22, 343, 10, 22, 12, 22, 14, 22, 346, 11, 22, 5, 22, 348, 10, 22, 3, 22, 3, 22, 3, 22, 3, 22, 6, 22, 354, 10, 22, 13, 22, 14, 22, 355, 3, 22, 3, 22, 5, 22, 360, 10, 22, 3, 23, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 3, 41, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 51, 3, 51, 3, 52, 3, 52, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3, 54, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58, 5, 58, 487, 10, 58, 3, 58, 3, 58, 3, 58, 7, 58, 492, 10, 58, 12, 58, 14, 58, 495, 11, 58, 3, 59, 3, 59, 3, 59, 3, 59, 7, 59, 501, 10, 59, 12, 59, 14, 59, 504, 11, 59, 3, 59, 3, 59, 3, 60, 3, 60, 3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 61, 3, 62, 3, 62, 3, 62, 3, 62, 3, 63, 3, 63, 3, 63, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 65, 3, 66, 3, 66, 3, 66, 3, 66, 3, 67, 6, 67, 540, 10, 67, 13, 67, 14, 67, 541, 3, 68, 6, 68, 545, 10, 68, 13, 68, 14, 68, 546, 3, 68, 3, 68, 5, 68, 551, 10, 68, 3, 69, 3, 69, 3, 70, 3, 70, 3, 70, 3, 70, 3, 71, 3, 71, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 72, 3, 73, 6, 73, 568, 10, 73, 13, 73, 14, 73, 569, 3, 73, 3, 73, 4, 244, 297, 2, 2, 74, 5, 2, 3, 7, 2, 4, 9, 2, 5, 11, 2, 6, 13, 2, 7, 15, 2, 8, 17, 2, 9, 19, 2, 10, 21, 2, 11, 23, 2, 12, 25, 2, 13, 27, 2, 14, 29, 2, 15, 31, 2, 2, 33, 2, 2, 35, 2, 2, 37, 2, 2, 39, 2, 2, 41, 2, 16, 43, 2, 17, 45, 2, 18, 47, 2, 19, 49, 2, 20, 51, 2, 21, 53, 2, 22, 55, 2, 23, 57, 2, 24, 59, 2, 25, 61, 2, 26, 63, 2, 27, 65, 2, 28, 67, 2, 29, 69, 2, 30, 71, 2, 31, 73, 2, 32, 75, 2, 33, 77, 2, 34, 79, 2, 35, 81, 2, 36, 83, 2, 37, 85, 2, 38, 87, 2, 39, 89, 2, 40, 91, 2, 41, 93, 2, 42, 95, 2, 43, 97, 2, 44, 99, 2, 45, 101, 2, 46, 103, 2, 47, 105, 2, 48, 107, 2, 49, 109, 2, 50, 111, 2, 51, 113, 2, 52, 115, 2, 53, 117, 2, 54, 119, 2, 55, 121, 2, 56, 123, 2, 57, 125, 2, 58, 127, 2, 2, 129, 2, 2, 131, 2, 2, 133, 2, 2, 135, 2, 59, 137, 2, 2, 139, 2, 60, 141, 2, 61, 143, 2, 62, 145, 2, 63, 147, 2, 64, 5, 2, 3, 4, 14, 4, 2, 12, 12, 15, 15, 5, 2, 11, 12, 15, 15, 34, 34, 3, 2, 50, 59, 4, 2, 67, 92, 99, 124, 7, 2, 36, 36, 94, 94, 112, 112, 116, 116, 118, 118, 6, 2, 12, 12, 15, 15, 36, 36, 94, 94, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47, 3, 2, 98, 98, 12, 2, 11, 12, 15, 15, 34, 34, 46, 46, 49, 49, 63, 63, 93, 93, 95, 95, 98, 98, 126, 126, 4, 2, 44, 44, 49, 49, 8, 2, 11, 12, 15, 15, 34, 34, 49, 49, 93, 93, 95, 95, 2, 599, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 3, 29, 3, 2, 2, 2, 3, 41, 3, 2, 2, 2, 3, 43, 3, 2, 2, 2, 3, 45, 3, 2, 2, 2, 3, 47, 3, 2, 2, 2, 3, 49, 3, 2, 2, 2, 3, 51, 3, 2, 2, 2, 3, 53, 3, 2, 2, 2, 3, 55, 3, 2, 2, 2, 3, 57, 3, 2, 2, 2, 3, 59, 3, 2, 2, 2, 3, 61, 3, 2, 2, 2, 3, 63, 3, 2, 2, 2, 3, 65, 3, 2, 2, 2, 3, 67, 3, 2, 2, 2, 3, 69, 3, 2, 2, 2, 3, 71, 3, 2, 2, 2, 3, 73, 3, 2, 2, 2, 3, 75, 3, 2, 2, 2, 3, 77, 3, 2, 2, 2, 3, 79, 3, 2, 2, 2, 3, 81, 3, 2, 2, 2, 3, 83, 3, 2, 2, 2, 3, 85, 3, 2, 2, 2, 3, 87, 3, 2, 2, 2, 3, 89, 3, 2, 2, 2, 3, 91, 3, 2, 2, 2, 3, 93, 3, 2, 2, 2, 3, 95, 3, 2, 2, 2, 3, 97, 3, 2, 2, 2, 3, 99, 3, 2, 2, 2, 3, 101, 3, 2, 2, 2, 3, 103, 3, 2, 2, 2, 3, 105, 3, 2, 2, 2, 3, 107, 3, 2, 2, 2, 3, 109, 3, 2, 2, 2, 3, 111, 3, 2, 2, 2, 3, 113, 3, 2, 2, 2, 3, 115, 3, 2, 2, 2, 3, 117, 3, 2, 2, 2, 3, 119, 3, 2, 2, 2, 3, 121, 3, 2, 2, 2, 3, 123, 3, 2, 2, 2, 3, 125, 3, 2, 2, 2, 4, 127, 3, 2, 2, 2, 4, 129, 3, 2, 2, 2, 4, 131, 3, 2, 2, 2, 4, 133, 3, 2, 2, 2, 4, 135, 3, 2, 2, 2, 4, 139, 3, 2, 2, 2, 4, 141, 3, 2, 2, 2, 4, 143, 3, 2, 2, 2, 4, 145, 3, 2, 2, 2, 4, 147, 3, 2, 2, 2, 5, 149, 3, 2, 2, 2, 7, 156, 3, 2, 2, 2, 9, 166, 3, 2, 2, 2, 11, 173, 3, 2, 2, 2, 13, 179, 3, 2, 2, 2, 15, 187, 3, 2, 2, 2, 17, 195, 3, 2, 2, 2, 19, 202, 3, 2, 2, 2, 21, 210, 3, 2, 2, 2, 23, 220, 3, 2, 2, 2, 25, 237, 3, 2, 2, 2, 27, 253, 3, 2, 2, 2, 29, 259, 3, 2, 2, 2, 31, 263, 3, 2, 2, 2, 33, 265, 3, 2, 2, 2, 35, 267, 3, 2, 2, 2, 37, 270, 3, 2, 2, 2, 39, 272, 3, 2, 2, 2, 41, 310, 3, 2, 2, 2, 43, 313, 3, 2, 2, 2, 45, 359, 3, 2, 2, 2, 47, 361, 3, 2, 2, 2, 49, 364, 3, 2, 2, 2, 51, 368, 3, 2, 2, 2, 53, 372, 3, 2, 2, 2, 55, 374, 3, 2, 2, 2, 57, 376, 3, 2, 2, 2, 59, 381, 3, 2, 2, 2, 61, 383, 3, 2, 2, 2, 63, 389, 3, 2, 2, 2, 65, 395, 3, 2, 2, 2, 67, 400, 3, 2, 2, 2, 69, 402, 3, 2, 2, 2, 71, 406, 3, 2, 2, 2, 73, 411, 3, 2, 2, 2, 75, 415, 3, 2, 2, 2, 77, 420, 3, 2, 2, 2, 79, 426, 3, 2, 2, 2, 81, 429, 3, 2, 2, 2, 83, 431, 3, 2, 2, 2, 85, 436, 3, 2, 2, 2, 87, 439, 3, 2, 2, 2, 89, 442, 3, 2, 2, 2, 91, 444, 3, 2, 2, 2, 93, 447, 3, 2, 2, 2, 95, 449, 3, 2, 2, 2, 97, 452, 3, 2, 2, 2, 99, 454, 3, 2, 2, 2, 101, 456, 3, 2, 2, 2, 103, 458, 3, 2, 2, 2, 105, 460, 3, 2, 2, 2, 107, 462, 3, 2, 2, 2, 109, 468, 3, 2, 2, 2, 111, 472, 3, 2, 2, 2, 113, 476, 3, 2, 2, 2, 115, 480, 3, 2, 2, 2, 117, 486, 3, 2, 2, 2, 119, 496, 3, 2, 2, 2, 121, 507, 3, 2, 2, 2, 123, 511, 3, 2, 2, 2, 125, 515, 3, 2, 2, 2, 127, 519, 3, 2, 2, 2, 129, 524, 3, 2, 2, 2, 131, 530, 3, 2, 2, 2, 133, 534, 3, 2, 2, 2, 135, 539, 3, 2, 2, 2, 137, 550, 3, 2, 2, 2, 139, 552, 3, 2, 2, 2, 141, 554, 3, 2, 2, 2, 143, 558, 3, 2, 2, 2, 145, 562, 3, 2, 2, 2, 147, 567, 3, 2, 2, 2, 149, 150, 7, 103, 2, 2, 150, 151, 7, 120, 2, 2, 151, 152, 7, 99, 2, 2, 152, 153, 7, 110, 2, 2, 153, 154, 3, 2, 2, 2, 154, 155, 8, 2, 2, 2, 155, 6, 3, 2, 2, 2, 156, 157, 7, 103, 2, 2, 157, 158, 7, 122, 2, 2, 158, 159, 7, 114, 2, 2, 159, 160, 7, 110, 2, 2, 160, 161, 7, 99, 2, 2, 161, 162, 7, 107, 2, 2, 162, 163, 7, 112, 2, 2, 163, 164, 3, 2, 2, 2, 164, 165, 8, 3, 2, 2, 165, 8, 3, 2, 2, 2, 166, 167, 7, 104, 2, 2, 167, 168, 7, 116, 2, 2, 168, 169, 7, 113, 2, 2, 169, 170, 7, 111, 2, 2, 170, 171, 3, 2, 2, 2, 171, 172, 8, 4, 3, 2, 172, 10, 3, 2, 2, 2, 173, 174, 7, 116, 2, 2, 174, 175, 7, 113, 2, 2, 175, 176, 7, 121, 2, 2, 176, 177, 3, 2, 2, 2, 177, 178, 8, 5, 2, 2, 178, 12, 3, 2, 2, 2, 179, 180, 7, 117, 2, 2, 180, 181, 7, 118, 2, 2, 181, 182, 7, 99, 2, 2, 182, 183, 7, 118, 2, 2, 183, 184, 7, 117, 2, 2, 184, 185, 3, 2, 2, 2, 185, 186, 8, 6, 2, 2, 186, 14, 3, 2, 2, 2, 187, 188, 7, 121, 2, 2, 188, 189, 7, 106, 2, 2, 189, 190, 7, 103, 2, 2, 190, 191, 7, 116, 2, 2, 191, 192, 7, 103, 2, 2, 192, 193, 3, 2, 2, 2, 193, 194, 8, 7, 2, 2, 194, 16, 3, 2, 2, 2, 195, 196, 7, 117, 2, 2, 196, 197, 7, 113, 2, 2, 197, 198, 7, 116, 2, 2, 198, 199, 7, 118, 2, 2, 199, 200, 3, 2, 2, 2, 200, 201, 8, 8, 2, 2, 201, 18, 3, 2, 2, 2, 202, 203, 7, 110, 2, 2, 203, 204, 7, 107, 2, 2, 204, 205, 7, 111, 2, 2, 205, 206, 7, 107, 2, 2, 206, 207, 7, 118, 2, 2, 207, 208, 3, 2, 2, 2, 208, 209, 8, 9, 2, 2, 209, 20, 3, 2, 2, 2, 210, 211, 7, 114, 2, 2, 211, 212, 7, 116, 2, 2, 212, 213, 7, 113, 2, 2, 213, 214, 7, 108, 2, 2, 214, 215, 7, 103, 2, 2, 215, 216, 7, 101, 2, 2, 216, 217, 7, 118, 2, 2, 217, 218, 3, 2, 2, 2, 218, 219, 8, 10, 3, 2, 219, 22, 3, 2, 2, 2, 220, 221, 7, 49, 2, 2, 221, 222, 7, 49, 2, 2, 222, 226, 3, 2, 2, 2, 223, 225, 10, 2, 2, 2, 224, 223, 3, 2, 2, 2, 225, 228, 3, 2, 2, 2, 226, 224, 3, 2, 2, 2, 226, 227, 3, 2, 2, 2, 227, 230, 3, 2, 2, 2, 228, 226, 3, 2, 2, 2, 229, 231, 7, 15, 2, 2, 230, 229, 3, 2, 2, 2, 230, 231, 3, 2, 2, 2, 231, 233, 3, 2, 2, 2, 232, 234, 7, 12, 2, 2, 233, 232, 3, 2, 2, 2, 233, 234, 3, 2, 2, 2, 234, 235, 3, 2, 2, 2, 235, 236, 8, 11, 4, 2, 236, 24, 3, 2, 2, 2, 237, 238, 7, 49, 2, 2, 238, 239, 7, 44, 2, 2, 239, 244, 3, 2, 2, 2, 240, 243, 5, 25, 12, 2, 241, 243, 11, 2, 2, 2, 242, 240, 3, 2, 2, 2, 242, 241, 3, 2, 2, 2, 243, 246, 3, 2, 2, 2, 244, 245, 3, 2, 2, 2, 244, 242, 3, 2, 2, 2, 245, 247, 3, 2, 2, 2, 246, 244, 3, 2, 2, 2, 247, 248, 7, 44, 2, 2, 248, 249, 7, 49, 2, 2, 249, 250, 3, 2, 2, 2, 250, 251, 8, 12, 4, 2, 251, 26, 3, 2, 2, 2, 252, 254, 9, 3, 2, 2, 253, 252, 3, 2, 2, 2, 254, 255, 3, 2, 2, 2, 255, 253, 3, 2, 2, 2, 255, 256, 3, 2, 2, 2, 256, 257, 3, 2, 2, 2, 257, 258, 8, 13, 4, 2, 258, 28, 3, 2, 2, 2, 259, 260, 7, 126, 2, 2, 260, 261, 3, 2, 2, 2, 261, 262, 8, 14, 5, 2, 262, 30, 3, 2, 2, 2, 263, 264, 9, 4, 2, 2, 264, 32, 3, 2, 2, 2, 265, 266, 9, 5, 2, 2, 266, 34, 3, 2, 2, 2, 267, 268, 7, 94, 2, 2, 268, 269, 9, 6, 2, 2, 269, 36, 3, 2, 2, 2, 270, 271, 10, 7, 2, 2, 271, 38, 3, 2, 2, 2, 272, 274, 9, 8, 2, 2, 273, 275, 9, 9, 2, 2, 274, 273, 3, 2, 2, 2, 274, 275, 3, 2, 2, 2, 275, 277, 3, 2, 2, 2, 276, 278, 5, 31, 15, 2, 277, 276, 3, 2, 2, 2, 278, 279, 3, 2, 2, 2, 279, 277, 3, 2, 2, 2, 279, 280, 3, 2, 2, 2, 280, 40, 3, 2, 2, 2, 281, 286, 7, 36, 2, 2, 282, 285, 5, 35, 17, 2, 283, 285, 5, 37, 18, 2, 284, 282, 3, 2, 2, 2, 284, 283, 3, 2, 2, 2, 285, 288, 3, 2, 2, 2, 286, 284, 3, 2, 2, 2, 286, 287, 3, 2, 2, 2, 287, 289, 3, 2, 2, 2, 288, 286, 3, 2, 2, 2, 289, 311, 7, 36, 2, 2, 290, 291, 7, 36, 2, 2, 291, 292, 7, 36, 2, 2, 292, 293, 7, 36, 2, 2, 293, 297, 3, 2, 2, 2, 294, 296, 10, 2, 2, 2, 295, 294, 3, 2, 2, 2, 296, 299, 3, 2, 2, 2, 297, 298, 3, 2, 2, 2, 297, 295, 3, 2, 2, 2, 298, 300, 3, 2, 2, 2, 299, 297, 3, 2, 2, 2, 300, 301, 7, 36, 2, 2, 301, 302, 7, 36, 2, 2, 302, 303, 7, 36, 2, 2, 303, 305, 3, 2, 2, 2, 304, 306, 7, 36, 2, 2, 305, 304, 3, 2, 2, 2, 305, 306, 3, 2, 2, 2, 306, 308, 3, 2, 2, 2, 307, 309, 7, 36, 2, 2, 308, 307, 3, 2, 2, 2, 308, 309, 3, 2, 2, 2, 309, 311, 3, 2, 2, 2, 310, 281, 3, 2, 2, 2, 310, 290, 3, 2, 2, 2, 311, 42, 3, 2, 2, 2, 312, 314, 5, 31, 15, 2, 313, 312, 3, 2, 2, 2, 314, 315, 3, 2, 2, 2, 315, 313, 3, 2, 2, 2, 315, 316, 3, 2, 2, 2, 316, 44, 3, 2, 2, 2, 317, 319, 5, 31, 15, 2, 318, 317, 3, 2, 2, 2, 319, 320, 3, 2, 2, 2, 320, 318, 3, 2, 2, 2, 320, 321, 3, 2, 2, 2, 321, 322, 3, 2, 2, 2, 322, 326, 5, 59, 29, 2, 323, 325, 5, 31, 15, 2, 324, 323, 3, 2, 2, 2, 325, 328, 3, 2, 2, 2, 326, 324, 3, 2, 2, 2, 326, 327, 3, 2, 2, 2, 327, 360, 3, 2, 2, 2, 328, 326, 3, 2, 2, 2, 329, 331, 5, 59, 29, 2, 330, 332, 5, 31, 15, 2, 331, 330, 3, 2, 2, 2, 332, 333, 3, 2, 2, 2, 333, 331, 3, 2, 2, 2, 333, 334, 3, 2, 2, 2, 334, 360, 3, 2, 2, 2, 335, 337, 5, 31, 15, 2, 336, 335, 3, 2, 2, 2, 337, 338, 3, 2, 2, 2, 338, 336, 3, 2, 2, 2, 338, 339, 3, 2, 2, 2, 339, 347, 3, 2, 2, 2, 340, 344, 5, 59, 29, 2, 341, 343, 5, 31, 15, 2, 342, 341, 3, 2, 2, 2, 343, 346, 3, 2, 2, 2, 344, 342, 3, 2, 2, 2, 344, 345, 3, 2, 2, 2, 345, 348, 3, 2, 2, 2, 346, 344, 3, 2, 2, 2, 347, 340, 3, 2, 2, 2, 347, 348, 3, 2, 2, 2, 348, 349, 3, 2, 2, 2, 349, 350, 5, 39, 19, 2, 350, 360, 3, 2, 2, 2, 351, 353, 5, 59, 29, 2, 352, 354, 5, 31, 15, 2, 353, 352, 3, 2, 2, 2, 354, 355, 3, 2, 2, 2, 355, 353, 3, 2, 2, 2, 355, 356, 3, 2, 2, 2, 356, 357, 3, 2, 2, 2, 357, 358, 5, 39, 19, 2, 358, 360, 3, 2, 2, 2, 359, 318, 3, 2, 2, 2, 359, 329, 3, 2, 2, 2, 359, 336, 3, 2, 2, 2, 359, 351, 3, 2, 2, 2, 360, 46, 3, 2, 2, 2, 361, 362, 7, 100, 2, 2, 362, 363, 7, 123, 2, 2, 363, 48, 3, 2, 2, 2, 364, 365, 7, 99, 2, 2, 365, 366, 7, 112, 2, 2, 366, 367, 7, 102, 2, 2, 367, 50, 3, 2, 2, 2, 368, 369, 7, 99, 2, 2, 369, 370, 7, 117, 2, 2, 370, 371, 7, 101, 2, 2, 371, 52, 3, 2, 2, 2, 372, 373, 7, 63, 2, 2, 373, 54, 3, 2, 2, 2, 374, 375, 7, 46, 2, 2, 375, 56, 3, 2, 2, 2, 376, 377, 7, 102, 2, 2, 377, 378, 7, 103, 2, 2, 378, 379, 7, 117, 2, 2, 379, 380, 7, 101, 2, 2, 380, 58, 3, 2, 2, 2, 381, 382, 7, 48, 2, 2, 382, 60, 3, 2, 2, 2, 383, 384, 7, 104, 2, 2, 384, 385, 7, 99, 2, 2, 385, 386, 7, 110, 2, 2, 386, 387, 7, 117, 2, 2, 387, 388, 7, 103, 2, 2, 388, 62, 3, 2, 2, 2, 389, 390, 7, 104, 2, 2, 390, 391, 7, 107, 2, 2, 391, 392, 7, 116, 2, 2, 392, 393, 7, 117, 2, 2, 393, 394, 7, 118, 2, 2, 394, 64, 3, 2, 2, 2, 395, 396, 7, 110, 2, 2, 396, 397, 7, 99, 2, 2, 397, 398, 7, 117, 2, 2, 398, 399, 7, 118, 2, 2, 399, 66, 3, 2, 2, 2, 400, 401, 7, 42, 2, 2, 401, 68, 3, 2, 2, 2, 402, 403, 7, 93, 2, 2, 403, 404, 3, 2, 2, 2, 404, 405, 8, 34, 6, 2, 405, 70, 3, 2, 2, 2, 406, 407, 7, 95, 2, 2, 407, 408, 3, 2, 2, 2, 408, 409, 8, 35, 5, 2, 409, 410, 8, 35, 5, 2, 410, 72, 3, 2, 2, 2, 411, 412, 7, 112, 2, 2, 412, 413, 7, 113, 2, 2, 413, 414, 7, 118, 2, 2, 414, 74, 3, 2, 2, 2, 415, 416, 7, 112, 2, 2, 416, 417, 7, 119, 2, 2, 417, 418, 7, 110, 2, 2, 418, 419, 7, 110, 2, 2, 419, 76, 3, 2, 2, 2, 420, 421, 7, 112, 2, 2, 421, 422, 7, 119, 2, 2, 422, 423, 7, 110, 2, 2, 423, 424, 7, 110, 2, 2, 424, 425, 7, 117, 2, 2, 425, 78, 3, 2, 2, 2, 426, 427, 7, 113, 2, 2, 427, 428, 7, 116, 2, 2, 428, 80, 3, 2, 2, 2, 429, 430, 7, 43, 2, 2, 430, 82, 3, 2, 2, 2, 431, 432, 7, 118, 2, 2, 432, 433, 7, 116, 2, 2, 433, 434, 7, 119, 2, 2, 434, 435, 7, 103, 2, 2, 435, 84, 3, 2, 2, 2, 436, 437, 7, 63, 2, 2, 437, 438, 7, 63, 2, 2, 438, 86, 3, 2, 2, 2, 439, 440, 7, 35, 2, 2, 440, 441, 7, 63, 2, 2, 441, 88, 3, 2, 2, 2, 442, 443, 7, 62, 2, 2, 443, 90, 3, 2, 2, 2, 444, 445, 7, 62, 2, 2, 445, 446, 7, 63, 2, 2, 446, 92, 3, 2, 2, 2, 447, 448, 7, 64, 2, 2, 448, 94, 3, 2, 2, 2, 449, 450, 7, 64, 2, 2, 450, 451, 7, 63, 2, 2, 451, 96, 3, 2, 2, 2, 452, 453, 7, 45, 2, 2, 453, 98, 3, 2, 2, 2, 454, 455, 7, 47, 2, 2, 455, 100, 3, 2, 2, 2, 456, 457, 7, 44, 2, 2, 457, 102, 3, 2, 2, 2, 458, 459, 7, 49, 2, 2, 459, 104, 3, 2, 2, 2, 460, 461, 7, 39, 2, 2, 461, 106, 3, 2, 2, 2, 462, 463, 7, 116, 2, 2, 463, 464, 7, 113, 2, 2, 464, 465, 7, 119, 2, 2, 465, 466, 7, 112, 2, 2, 466, 467, 7, 102, 2, 2, 467, 108, 3, 2, 2, 2, 468, 469, 7, 99, 2, 2, 469, 470, 7, 120, 2, 2, 470, 471, 7, 105, 2, 2, 471, 110, 3, 2, 2, 2, 472, 473, 7, 117, 2, 2, 473, 474, 7, 119, 2, 2, 474, 475, 7, 111, 2, 2, 475, 112, 3, 2, 2, 2, 476, 477, 7, 111, 2, 2, 477, 478, 7, 107, 2, 2, 478, 479, 7, 112, 2, 2, 479, 114, 3, 2, 2, 2, 480, 481, 7, 111, 2, 2, 481, 482, 7, 99, 2, 2, 482, 483, 7, 122, 2, 2, 483, 116, 3, 2, 2, 2, 484, 487, 5, 33, 16, 2, 485, 487, 7, 97, 2, 2, 486, 484, 3, 2, 2, 2, 486, 485, 3, 2, 2, 2, 487, 493, 3, 2, 2, 2, 488, 492, 5, 33, 16, 2, 489, 492, 5, 31, 15, 2, 490, 492, 7, 97, 2, 2, 491, 488, 3, 2, 2, 2, 491, 489, 3, 2, 2, 2, 491, 490, 3, 2, 2, 2, 492, 495, 3, 2, 2, 2, 493, 491, 3, 2, 2, 2, 493, 494, 3, 2, 2, 2, 494, 118, 3, 2, 2, 2, 495, 493, 3, 2, 2, 2, 496, 502, 7, 98, 2, 2, 497, 501, 10, 10, 2, 2, 498, 499, 7, 98, 2, 2, 499, 501, 7, 98, 2, 2, 500, 497, 3, 2, 2, 2, 500, 498, 3, 2, 2, 2, 501, 504, 3, 2, 2, 2, 502, 500, 3, 2, 2, 2, 502, 503, 3, 2, 2, 2, 503, 505, 3, 2, 2, 2, 504, 502, 3, 2, 2, 2, 505, 506, 7, 98, 2, 2, 506, 120, 3, 2, 2, 2, 507, 508, 5, 23, 11, 2, 508, 509, 3, 2, 2, 2, 509, 510, 8, 60, 4, 2, 510, 122, 3, 2, 2, 2, 511, 512, 5, 25, 12, 2, 512, 513, 3, 2, 2, 2, 513, 514, 8, 61, 4, 2, 514, 124, 3, 2, 2, 2, 515, 516, 5, 27, 13, 2, 516, 517, 3, 2, 2, 2, 517, 518, 8, 62, 4, 2, 518, 126, 3, 2, 2, 2, 519, 520, 7, 126, 2, 2, 520, 521, 3, 2, 2, 2, 521, 522, 8, 63, 7, 2, 522, 523, 8, 63, 5, 2, 523, 128, 3, 2, 2, 2, 524, 525, 7, 95, 2, 2, 525, 526, 3, 2, 2, 2, 526, 527, 8, 64, 5, 2, 527, 528, 8, 64, 5, 2, 528, 529, 8, 64, 8, 2, 529, 130, 3, 2, 2, 2, 530, 531, 7, 46, 2, 2, 531, 532, 3, 2, 2, 2, 532, 533, 8, 65, 9, 2, 533, 132, 3, 2, 2, 2, 534, 535, 7, 63, 2, 2, 535, 536, 3, 2, 2, 2, 536, 537, 8, 66, 10, 2, 537, 134, 3, 2, 2, 2, 538, 540, 5, 137, 68, 2, 539, 538, 3, 2, 2, 2, 540, 541, 3, 2, 2, 2, 541, 539, 3, 2, 2, 2, 541, 542, 3, 2, 2, 2, 542, 136, 3, 2, 2, 2, 543, 545, 10, 11, 2, 2, 544, 543, 3, 2, 2, 2, 545, 546, 3, 2, 2, 2, 546, 544, 3, 2, 2, 2, 546, 547, 3, 2, 2, 2, 547, 551, 3, 2, 2, 2, 548, 549, 7, 49, 2, 2, 549, 551, 10, 12, 2, 2, 550, 544, 3, 2, 2, 2, 550, 548, 3, 2, 2, 2, 551, 138, 3, 2, 2, 2, 552, 553, 5, 119, 59, 2, 553, 140, 3, 2, 2, 2, 554, 555, 5, 23, 11, 2, 555, 556, 3, 2, 2, 2, 556, 557, 8, 70, 4, 2, 557, 142, 3, 2, 2, 2, 558, 559, 5, 25, 12, 2, 559, 560, 3, 2, 2, 2, 560, 561, 8, 71, 4, 2, 561, 144, 3, 2, 2, 2, 562, 563, 5, 27, 13, 2, 563, 564, 3, 2, 2, 2, 564, 565, 8, 72, 4, 2, 565, 146, 3, 2, 2, 2, 566, 568, 10, 13, 2, 2, 567, 566, 3, 2, 2, 2, 568, 569, 3, 2, 2, 2, 569, 567, 3, 2, 2, 2, 569, 570, 3, 2, 2, 2, 570, 571, 3, 2, 2, 2, 571, 572, 8, 73, 2, 2, 572, 148, 3, 2, 2, 2, 37, 2, 3, 4, 226, 230, 233, 242, 244, 255, 274, 279, 284, 286, 297, 305, 308, 310, 315, 320, 326, 333, 338, 344, 347, 355, 359, 486, 491, 493, 500, 502, 541, 546, 550, 569, 11, 7, 3, 2, 7, 4, 2, 2, 3, 2, 6, 2, 2, 7, 2, 2, 9, 15, 2, 9, 31, 2, 9, 23, 2, 9, 22, 2] \ No newline at end of file +[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 2, 51, 533, 8, 1, 8, 1, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 7, 11, 199, 10, 11, 12, 11, 14, 11, 202, 11, 11, 3, 11, 5, 11, 205, 10, 11, 3, 11, 5, 11, 208, 10, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 7, 12, 217, 10, 12, 12, 12, 14, 12, 220, 11, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 6, 13, 228, 10, 13, 13, 13, 14, 13, 229, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 19, 3, 19, 5, 19, 249, 10, 19, 3, 19, 6, 19, 252, 10, 19, 13, 19, 14, 19, 253, 3, 20, 3, 20, 3, 20, 7, 20, 259, 10, 20, 12, 20, 14, 20, 262, 11, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 7, 20, 270, 10, 20, 12, 20, 14, 20, 273, 11, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 5, 20, 280, 10, 20, 3, 20, 5, 20, 283, 10, 20, 5, 20, 285, 10, 20, 3, 21, 6, 21, 288, 10, 21, 13, 21, 14, 21, 289, 3, 22, 6, 22, 293, 10, 22, 13, 22, 14, 22, 294, 3, 22, 3, 22, 7, 22, 299, 10, 22, 12, 22, 14, 22, 302, 11, 22, 3, 22, 3, 22, 6, 22, 306, 10, 22, 13, 22, 14, 22, 307, 3, 22, 6, 22, 311, 10, 22, 13, 22, 14, 22, 312, 3, 22, 3, 22, 7, 22, 317, 10, 22, 12, 22, 14, 22, 320, 11, 22, 5, 22, 322, 10, 22, 3, 22, 3, 22, 3, 22, 3, 22, 6, 22, 328, 10, 22, 13, 22, 14, 22, 329, 3, 22, 3, 22, 5, 22, 334, 10, 22, 3, 23, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 29, 3, 29, 3, 30, 3, 30, 3, 30, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 3, 35, 5, 35, 383, 10, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 5, 36, 395, 10, 36, 3, 37, 3, 37, 3, 38, 3, 38, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 42, 3, 42, 3, 42, 3, 42, 5, 42, 414, 10, 42, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 5, 44, 431, 10, 44, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 3, 45, 5, 45, 450, 10, 45, 3, 46, 3, 46, 5, 46, 454, 10, 46, 3, 46, 3, 46, 3, 46, 7, 46, 459, 10, 46, 12, 46, 14, 46, 462, 11, 46, 3, 47, 3, 47, 3, 47, 3, 47, 7, 47, 468, 10, 47, 12, 47, 14, 47, 471, 11, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3, 54, 3, 54, 3, 54, 3, 55, 6, 55, 507, 10, 55, 13, 55, 14, 55, 508, 3, 56, 6, 56, 512, 10, 56, 13, 56, 14, 56, 513, 3, 56, 3, 56, 5, 56, 518, 10, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 59, 3, 60, 3, 60, 3, 60, 3, 60, 4, 218, 271, 2, 2, 61, 5, 2, 3, 7, 2, 4, 9, 2, 5, 11, 2, 6, 13, 2, 7, 15, 2, 8, 17, 2, 9, 19, 2, 10, 21, 2, 11, 23, 2, 12, 25, 2, 13, 27, 2, 14, 29, 2, 15, 31, 2, 2, 33, 2, 2, 35, 2, 2, 37, 2, 2, 39, 2, 2, 41, 2, 16, 43, 2, 17, 45, 2, 18, 47, 2, 19, 49, 2, 20, 51, 2, 21, 53, 2, 22, 55, 2, 23, 57, 2, 24, 59, 2, 25, 61, 2, 26, 63, 2, 27, 65, 2, 28, 67, 2, 29, 69, 2, 30, 71, 2, 31, 73, 2, 32, 75, 2, 33, 77, 2, 34, 79, 2, 35, 81, 2, 36, 83, 2, 37, 85, 2, 38, 87, 2, 39, 89, 2, 40, 91, 2, 41, 93, 2, 42, 95, 2, 43, 97, 2, 44, 99, 2, 45, 101, 2, 46, 103, 2, 2, 105, 2, 2, 107, 2, 2, 109, 2, 2, 111, 2, 47, 113, 2, 2, 115, 2, 48, 117, 2, 49, 119, 2, 50, 121, 2, 51, 5, 2, 3, 4, 13, 4, 2, 12, 12, 15, 15, 5, 2, 11, 12, 15, 15, 34, 34, 3, 2, 50, 59, 4, 2, 67, 92, 99, 124, 7, 2, 36, 36, 94, 94, 112, 112, 116, 116, 118, 118, 6, 2, 12, 12, 15, 15, 36, 36, 94, 94, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47, 3, 2, 98, 98, 12, 2, 11, 12, 15, 15, 34, 34, 46, 46, 49, 49, 63, 63, 93, 93, 95, 95, 98, 98, 126, 126, 4, 2, 44, 44, 49, 49, 2, 570, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 3, 29, 3, 2, 2, 2, 3, 41, 3, 2, 2, 2, 3, 43, 3, 2, 2, 2, 3, 45, 3, 2, 2, 2, 3, 47, 3, 2, 2, 2, 3, 49, 3, 2, 2, 2, 3, 51, 3, 2, 2, 2, 3, 53, 3, 2, 2, 2, 3, 55, 3, 2, 2, 2, 3, 57, 3, 2, 2, 2, 3, 59, 3, 2, 2, 2, 3, 61, 3, 2, 2, 2, 3, 63, 3, 2, 2, 2, 3, 65, 3, 2, 2, 2, 3, 67, 3, 2, 2, 2, 3, 69, 3, 2, 2, 2, 3, 71, 3, 2, 2, 2, 3, 73, 3, 2, 2, 2, 3, 75, 3, 2, 2, 2, 3, 77, 3, 2, 2, 2, 3, 79, 3, 2, 2, 2, 3, 81, 3, 2, 2, 2, 3, 83, 3, 2, 2, 2, 3, 85, 3, 2, 2, 2, 3, 87, 3, 2, 2, 2, 3, 89, 3, 2, 2, 2, 3, 91, 3, 2, 2, 2, 3, 93, 3, 2, 2, 2, 3, 95, 3, 2, 2, 2, 3, 97, 3, 2, 2, 2, 3, 99, 3, 2, 2, 2, 3, 101, 3, 2, 2, 2, 4, 103, 3, 2, 2, 2, 4, 105, 3, 2, 2, 2, 4, 107, 3, 2, 2, 2, 4, 109, 3, 2, 2, 2, 4, 111, 3, 2, 2, 2, 4, 115, 3, 2, 2, 2, 4, 117, 3, 2, 2, 2, 4, 119, 3, 2, 2, 2, 4, 121, 3, 2, 2, 2, 5, 123, 3, 2, 2, 2, 7, 130, 3, 2, 2, 2, 9, 140, 3, 2, 2, 2, 11, 147, 3, 2, 2, 2, 13, 153, 3, 2, 2, 2, 15, 161, 3, 2, 2, 2, 17, 169, 3, 2, 2, 2, 19, 176, 3, 2, 2, 2, 21, 184, 3, 2, 2, 2, 23, 194, 3, 2, 2, 2, 25, 211, 3, 2, 2, 2, 27, 227, 3, 2, 2, 2, 29, 233, 3, 2, 2, 2, 31, 237, 3, 2, 2, 2, 33, 239, 3, 2, 2, 2, 35, 241, 3, 2, 2, 2, 37, 244, 3, 2, 2, 2, 39, 246, 3, 2, 2, 2, 41, 284, 3, 2, 2, 2, 43, 287, 3, 2, 2, 2, 45, 333, 3, 2, 2, 2, 47, 335, 3, 2, 2, 2, 49, 338, 3, 2, 2, 2, 51, 342, 3, 2, 2, 2, 53, 344, 3, 2, 2, 2, 55, 346, 3, 2, 2, 2, 57, 348, 3, 2, 2, 2, 59, 350, 3, 2, 2, 2, 61, 354, 3, 2, 2, 2, 63, 359, 3, 2, 2, 2, 65, 363, 3, 2, 2, 2, 67, 368, 3, 2, 2, 2, 69, 371, 3, 2, 2, 2, 71, 382, 3, 2, 2, 2, 73, 394, 3, 2, 2, 2, 75, 396, 3, 2, 2, 2, 77, 398, 3, 2, 2, 2, 79, 400, 3, 2, 2, 2, 81, 402, 3, 2, 2, 2, 83, 404, 3, 2, 2, 2, 85, 413, 3, 2, 2, 2, 87, 415, 3, 2, 2, 2, 89, 430, 3, 2, 2, 2, 91, 449, 3, 2, 2, 2, 93, 453, 3, 2, 2, 2, 95, 463, 3, 2, 2, 2, 97, 474, 3, 2, 2, 2, 99, 478, 3, 2, 2, 2, 101, 482, 3, 2, 2, 2, 103, 486, 3, 2, 2, 2, 105, 491, 3, 2, 2, 2, 107, 497, 3, 2, 2, 2, 109, 501, 3, 2, 2, 2, 111, 506, 3, 2, 2, 2, 113, 517, 3, 2, 2, 2, 115, 519, 3, 2, 2, 2, 117, 521, 3, 2, 2, 2, 119, 525, 3, 2, 2, 2, 121, 529, 3, 2, 2, 2, 123, 124, 7, 103, 2, 2, 124, 125, 7, 120, 2, 2, 125, 126, 7, 99, 2, 2, 126, 127, 7, 110, 2, 2, 127, 128, 3, 2, 2, 2, 128, 129, 8, 2, 2, 2, 129, 6, 3, 2, 2, 2, 130, 131, 7, 103, 2, 2, 131, 132, 7, 122, 2, 2, 132, 133, 7, 114, 2, 2, 133, 134, 7, 110, 2, 2, 134, 135, 7, 99, 2, 2, 135, 136, 7, 107, 2, 2, 136, 137, 7, 112, 2, 2, 137, 138, 3, 2, 2, 2, 138, 139, 8, 3, 2, 2, 139, 8, 3, 2, 2, 2, 140, 141, 7, 104, 2, 2, 141, 142, 7, 116, 2, 2, 142, 143, 7, 113, 2, 2, 143, 144, 7, 111, 2, 2, 144, 145, 3, 2, 2, 2, 145, 146, 8, 4, 3, 2, 146, 10, 3, 2, 2, 2, 147, 148, 7, 116, 2, 2, 148, 149, 7, 113, 2, 2, 149, 150, 7, 121, 2, 2, 150, 151, 3, 2, 2, 2, 151, 152, 8, 5, 2, 2, 152, 12, 3, 2, 2, 2, 153, 154, 7, 117, 2, 2, 154, 155, 7, 118, 2, 2, 155, 156, 7, 99, 2, 2, 156, 157, 7, 118, 2, 2, 157, 158, 7, 117, 2, 2, 158, 159, 3, 2, 2, 2, 159, 160, 8, 6, 2, 2, 160, 14, 3, 2, 2, 2, 161, 162, 7, 121, 2, 2, 162, 163, 7, 106, 2, 2, 163, 164, 7, 103, 2, 2, 164, 165, 7, 116, 2, 2, 165, 166, 7, 103, 2, 2, 166, 167, 3, 2, 2, 2, 167, 168, 8, 7, 2, 2, 168, 16, 3, 2, 2, 2, 169, 170, 7, 117, 2, 2, 170, 171, 7, 113, 2, 2, 171, 172, 7, 116, 2, 2, 172, 173, 7, 118, 2, 2, 173, 174, 3, 2, 2, 2, 174, 175, 8, 8, 2, 2, 175, 18, 3, 2, 2, 2, 176, 177, 7, 110, 2, 2, 177, 178, 7, 107, 2, 2, 178, 179, 7, 111, 2, 2, 179, 180, 7, 107, 2, 2, 180, 181, 7, 118, 2, 2, 181, 182, 3, 2, 2, 2, 182, 183, 8, 9, 2, 2, 183, 20, 3, 2, 2, 2, 184, 185, 7, 114, 2, 2, 185, 186, 7, 116, 2, 2, 186, 187, 7, 113, 2, 2, 187, 188, 7, 108, 2, 2, 188, 189, 7, 103, 2, 2, 189, 190, 7, 101, 2, 2, 190, 191, 7, 118, 2, 2, 191, 192, 3, 2, 2, 2, 192, 193, 8, 10, 3, 2, 193, 22, 3, 2, 2, 2, 194, 195, 7, 49, 2, 2, 195, 196, 7, 49, 2, 2, 196, 200, 3, 2, 2, 2, 197, 199, 10, 2, 2, 2, 198, 197, 3, 2, 2, 2, 199, 202, 3, 2, 2, 2, 200, 198, 3, 2, 2, 2, 200, 201, 3, 2, 2, 2, 201, 204, 3, 2, 2, 2, 202, 200, 3, 2, 2, 2, 203, 205, 7, 15, 2, 2, 204, 203, 3, 2, 2, 2, 204, 205, 3, 2, 2, 2, 205, 207, 3, 2, 2, 2, 206, 208, 7, 12, 2, 2, 207, 206, 3, 2, 2, 2, 207, 208, 3, 2, 2, 2, 208, 209, 3, 2, 2, 2, 209, 210, 8, 11, 4, 2, 210, 24, 3, 2, 2, 2, 211, 212, 7, 49, 2, 2, 212, 213, 7, 44, 2, 2, 213, 218, 3, 2, 2, 2, 214, 217, 5, 25, 12, 2, 215, 217, 11, 2, 2, 2, 216, 214, 3, 2, 2, 2, 216, 215, 3, 2, 2, 2, 217, 220, 3, 2, 2, 2, 218, 219, 3, 2, 2, 2, 218, 216, 3, 2, 2, 2, 219, 221, 3, 2, 2, 2, 220, 218, 3, 2, 2, 2, 221, 222, 7, 44, 2, 2, 222, 223, 7, 49, 2, 2, 223, 224, 3, 2, 2, 2, 224, 225, 8, 12, 4, 2, 225, 26, 3, 2, 2, 2, 226, 228, 9, 3, 2, 2, 227, 226, 3, 2, 2, 2, 228, 229, 3, 2, 2, 2, 229, 227, 3, 2, 2, 2, 229, 230, 3, 2, 2, 2, 230, 231, 3, 2, 2, 2, 231, 232, 8, 13, 4, 2, 232, 28, 3, 2, 2, 2, 233, 234, 7, 126, 2, 2, 234, 235, 3, 2, 2, 2, 235, 236, 8, 14, 5, 2, 236, 30, 3, 2, 2, 2, 237, 238, 9, 4, 2, 2, 238, 32, 3, 2, 2, 2, 239, 240, 9, 5, 2, 2, 240, 34, 3, 2, 2, 2, 241, 242, 7, 94, 2, 2, 242, 243, 9, 6, 2, 2, 243, 36, 3, 2, 2, 2, 244, 245, 10, 7, 2, 2, 245, 38, 3, 2, 2, 2, 246, 248, 9, 8, 2, 2, 247, 249, 9, 9, 2, 2, 248, 247, 3, 2, 2, 2, 248, 249, 3, 2, 2, 2, 249, 251, 3, 2, 2, 2, 250, 252, 5, 31, 15, 2, 251, 250, 3, 2, 2, 2, 252, 253, 3, 2, 2, 2, 253, 251, 3, 2, 2, 2, 253, 254, 3, 2, 2, 2, 254, 40, 3, 2, 2, 2, 255, 260, 7, 36, 2, 2, 256, 259, 5, 35, 17, 2, 257, 259, 5, 37, 18, 2, 258, 256, 3, 2, 2, 2, 258, 257, 3, 2, 2, 2, 259, 262, 3, 2, 2, 2, 260, 258, 3, 2, 2, 2, 260, 261, 3, 2, 2, 2, 261, 263, 3, 2, 2, 2, 262, 260, 3, 2, 2, 2, 263, 285, 7, 36, 2, 2, 264, 265, 7, 36, 2, 2, 265, 266, 7, 36, 2, 2, 266, 267, 7, 36, 2, 2, 267, 271, 3, 2, 2, 2, 268, 270, 10, 2, 2, 2, 269, 268, 3, 2, 2, 2, 270, 273, 3, 2, 2, 2, 271, 272, 3, 2, 2, 2, 271, 269, 3, 2, 2, 2, 272, 274, 3, 2, 2, 2, 273, 271, 3, 2, 2, 2, 274, 275, 7, 36, 2, 2, 275, 276, 7, 36, 2, 2, 276, 277, 7, 36, 2, 2, 277, 279, 3, 2, 2, 2, 278, 280, 7, 36, 2, 2, 279, 278, 3, 2, 2, 2, 279, 280, 3, 2, 2, 2, 280, 282, 3, 2, 2, 2, 281, 283, 7, 36, 2, 2, 282, 281, 3, 2, 2, 2, 282, 283, 3, 2, 2, 2, 283, 285, 3, 2, 2, 2, 284, 255, 3, 2, 2, 2, 284, 264, 3, 2, 2, 2, 285, 42, 3, 2, 2, 2, 286, 288, 5, 31, 15, 2, 287, 286, 3, 2, 2, 2, 288, 289, 3, 2, 2, 2, 289, 287, 3, 2, 2, 2, 289, 290, 3, 2, 2, 2, 290, 44, 3, 2, 2, 2, 291, 293, 5, 31, 15, 2, 292, 291, 3, 2, 2, 2, 293, 294, 3, 2, 2, 2, 294, 292, 3, 2, 2, 2, 294, 295, 3, 2, 2, 2, 295, 296, 3, 2, 2, 2, 296, 300, 5, 55, 27, 2, 297, 299, 5, 31, 15, 2, 298, 297, 3, 2, 2, 2, 299, 302, 3, 2, 2, 2, 300, 298, 3, 2, 2, 2, 300, 301, 3, 2, 2, 2, 301, 334, 3, 2, 2, 2, 302, 300, 3, 2, 2, 2, 303, 305, 5, 55, 27, 2, 304, 306, 5, 31, 15, 2, 305, 304, 3, 2, 2, 2, 306, 307, 3, 2, 2, 2, 307, 305, 3, 2, 2, 2, 307, 308, 3, 2, 2, 2, 308, 334, 3, 2, 2, 2, 309, 311, 5, 31, 15, 2, 310, 309, 3, 2, 2, 2, 311, 312, 3, 2, 2, 2, 312, 310, 3, 2, 2, 2, 312, 313, 3, 2, 2, 2, 313, 321, 3, 2, 2, 2, 314, 318, 5, 55, 27, 2, 315, 317, 5, 31, 15, 2, 316, 315, 3, 2, 2, 2, 317, 320, 3, 2, 2, 2, 318, 316, 3, 2, 2, 2, 318, 319, 3, 2, 2, 2, 319, 322, 3, 2, 2, 2, 320, 318, 3, 2, 2, 2, 321, 314, 3, 2, 2, 2, 321, 322, 3, 2, 2, 2, 322, 323, 3, 2, 2, 2, 323, 324, 5, 39, 19, 2, 324, 334, 3, 2, 2, 2, 325, 327, 5, 55, 27, 2, 326, 328, 5, 31, 15, 2, 327, 326, 3, 2, 2, 2, 328, 329, 3, 2, 2, 2, 329, 327, 3, 2, 2, 2, 329, 330, 3, 2, 2, 2, 330, 331, 3, 2, 2, 2, 331, 332, 5, 39, 19, 2, 332, 334, 3, 2, 2, 2, 333, 292, 3, 2, 2, 2, 333, 303, 3, 2, 2, 2, 333, 310, 3, 2, 2, 2, 333, 325, 3, 2, 2, 2, 334, 46, 3, 2, 2, 2, 335, 336, 7, 100, 2, 2, 336, 337, 7, 123, 2, 2, 337, 48, 3, 2, 2, 2, 338, 339, 7, 99, 2, 2, 339, 340, 7, 112, 2, 2, 340, 341, 7, 102, 2, 2, 341, 50, 3, 2, 2, 2, 342, 343, 7, 63, 2, 2, 343, 52, 3, 2, 2, 2, 344, 345, 7, 46, 2, 2, 345, 54, 3, 2, 2, 2, 346, 347, 7, 48, 2, 2, 347, 56, 3, 2, 2, 2, 348, 349, 7, 42, 2, 2, 349, 58, 3, 2, 2, 2, 350, 351, 7, 93, 2, 2, 351, 352, 3, 2, 2, 2, 352, 353, 8, 29, 6, 2, 353, 60, 3, 2, 2, 2, 354, 355, 7, 95, 2, 2, 355, 356, 3, 2, 2, 2, 356, 357, 8, 30, 5, 2, 357, 358, 8, 30, 5, 2, 358, 62, 3, 2, 2, 2, 359, 360, 7, 112, 2, 2, 360, 361, 7, 113, 2, 2, 361, 362, 7, 118, 2, 2, 362, 64, 3, 2, 2, 2, 363, 364, 7, 112, 2, 2, 364, 365, 7, 119, 2, 2, 365, 366, 7, 110, 2, 2, 366, 367, 7, 110, 2, 2, 367, 66, 3, 2, 2, 2, 368, 369, 7, 113, 2, 2, 369, 370, 7, 116, 2, 2, 370, 68, 3, 2, 2, 2, 371, 372, 7, 43, 2, 2, 372, 70, 3, 2, 2, 2, 373, 374, 7, 118, 2, 2, 374, 375, 7, 116, 2, 2, 375, 376, 7, 119, 2, 2, 376, 383, 7, 103, 2, 2, 377, 378, 7, 104, 2, 2, 378, 379, 7, 99, 2, 2, 379, 380, 7, 110, 2, 2, 380, 381, 7, 117, 2, 2, 381, 383, 7, 103, 2, 2, 382, 373, 3, 2, 2, 2, 382, 377, 3, 2, 2, 2, 383, 72, 3, 2, 2, 2, 384, 385, 7, 63, 2, 2, 385, 395, 7, 63, 2, 2, 386, 387, 7, 35, 2, 2, 387, 395, 7, 63, 2, 2, 388, 395, 7, 62, 2, 2, 389, 390, 7, 62, 2, 2, 390, 395, 7, 63, 2, 2, 391, 395, 7, 64, 2, 2, 392, 393, 7, 64, 2, 2, 393, 395, 7, 63, 2, 2, 394, 384, 3, 2, 2, 2, 394, 386, 3, 2, 2, 2, 394, 388, 3, 2, 2, 2, 394, 389, 3, 2, 2, 2, 394, 391, 3, 2, 2, 2, 394, 392, 3, 2, 2, 2, 395, 74, 3, 2, 2, 2, 396, 397, 7, 45, 2, 2, 397, 76, 3, 2, 2, 2, 398, 399, 7, 47, 2, 2, 399, 78, 3, 2, 2, 2, 400, 401, 7, 44, 2, 2, 401, 80, 3, 2, 2, 2, 402, 403, 7, 49, 2, 2, 403, 82, 3, 2, 2, 2, 404, 405, 7, 39, 2, 2, 405, 84, 3, 2, 2, 2, 406, 407, 7, 99, 2, 2, 407, 408, 7, 117, 2, 2, 408, 414, 7, 101, 2, 2, 409, 410, 7, 102, 2, 2, 410, 411, 7, 103, 2, 2, 411, 412, 7, 117, 2, 2, 412, 414, 7, 101, 2, 2, 413, 406, 3, 2, 2, 2, 413, 409, 3, 2, 2, 2, 414, 86, 3, 2, 2, 2, 415, 416, 7, 112, 2, 2, 416, 417, 7, 119, 2, 2, 417, 418, 7, 110, 2, 2, 418, 419, 7, 110, 2, 2, 419, 420, 7, 117, 2, 2, 420, 88, 3, 2, 2, 2, 421, 422, 7, 104, 2, 2, 422, 423, 7, 107, 2, 2, 423, 424, 7, 116, 2, 2, 424, 425, 7, 117, 2, 2, 425, 431, 7, 118, 2, 2, 426, 427, 7, 110, 2, 2, 427, 428, 7, 99, 2, 2, 428, 429, 7, 117, 2, 2, 429, 431, 7, 118, 2, 2, 430, 421, 3, 2, 2, 2, 430, 426, 3, 2, 2, 2, 431, 90, 3, 2, 2, 2, 432, 433, 7, 116, 2, 2, 433, 434, 7, 113, 2, 2, 434, 435, 7, 119, 2, 2, 435, 436, 7, 112, 2, 2, 436, 450, 7, 102, 2, 2, 437, 438, 7, 99, 2, 2, 438, 439, 7, 120, 2, 2, 439, 450, 7, 105, 2, 2, 440, 441, 7, 111, 2, 2, 441, 442, 7, 107, 2, 2, 442, 450, 7, 112, 2, 2, 443, 444, 7, 111, 2, 2, 444, 445, 7, 99, 2, 2, 445, 450, 7, 122, 2, 2, 446, 447, 7, 117, 2, 2, 447, 448, 7, 119, 2, 2, 448, 450, 7, 111, 2, 2, 449, 432, 3, 2, 2, 2, 449, 437, 3, 2, 2, 2, 449, 440, 3, 2, 2, 2, 449, 443, 3, 2, 2, 2, 449, 446, 3, 2, 2, 2, 450, 92, 3, 2, 2, 2, 451, 454, 5, 33, 16, 2, 452, 454, 7, 97, 2, 2, 453, 451, 3, 2, 2, 2, 453, 452, 3, 2, 2, 2, 454, 460, 3, 2, 2, 2, 455, 459, 5, 33, 16, 2, 456, 459, 5, 31, 15, 2, 457, 459, 7, 97, 2, 2, 458, 455, 3, 2, 2, 2, 458, 456, 3, 2, 2, 2, 458, 457, 3, 2, 2, 2, 459, 462, 3, 2, 2, 2, 460, 458, 3, 2, 2, 2, 460, 461, 3, 2, 2, 2, 461, 94, 3, 2, 2, 2, 462, 460, 3, 2, 2, 2, 463, 469, 7, 98, 2, 2, 464, 468, 10, 10, 2, 2, 465, 466, 7, 98, 2, 2, 466, 468, 7, 98, 2, 2, 467, 464, 3, 2, 2, 2, 467, 465, 3, 2, 2, 2, 468, 471, 3, 2, 2, 2, 469, 467, 3, 2, 2, 2, 469, 470, 3, 2, 2, 2, 470, 472, 3, 2, 2, 2, 471, 469, 3, 2, 2, 2, 472, 473, 7, 98, 2, 2, 473, 96, 3, 2, 2, 2, 474, 475, 5, 23, 11, 2, 475, 476, 3, 2, 2, 2, 476, 477, 8, 48, 4, 2, 477, 98, 3, 2, 2, 2, 478, 479, 5, 25, 12, 2, 479, 480, 3, 2, 2, 2, 480, 481, 8, 49, 4, 2, 481, 100, 3, 2, 2, 2, 482, 483, 5, 27, 13, 2, 483, 484, 3, 2, 2, 2, 484, 485, 8, 50, 4, 2, 485, 102, 3, 2, 2, 2, 486, 487, 7, 126, 2, 2, 487, 488, 3, 2, 2, 2, 488, 489, 8, 51, 7, 2, 489, 490, 8, 51, 5, 2, 490, 104, 3, 2, 2, 2, 491, 492, 7, 95, 2, 2, 492, 493, 3, 2, 2, 2, 493, 494, 8, 52, 5, 2, 494, 495, 8, 52, 5, 2, 495, 496, 8, 52, 8, 2, 496, 106, 3, 2, 2, 2, 497, 498, 7, 46, 2, 2, 498, 499, 3, 2, 2, 2, 499, 500, 8, 53, 9, 2, 500, 108, 3, 2, 2, 2, 501, 502, 7, 63, 2, 2, 502, 503, 3, 2, 2, 2, 503, 504, 8, 54, 10, 2, 504, 110, 3, 2, 2, 2, 505, 507, 5, 113, 56, 2, 506, 505, 3, 2, 2, 2, 507, 508, 3, 2, 2, 2, 508, 506, 3, 2, 2, 2, 508, 509, 3, 2, 2, 2, 509, 112, 3, 2, 2, 2, 510, 512, 10, 11, 2, 2, 511, 510, 3, 2, 2, 2, 512, 513, 3, 2, 2, 2, 513, 511, 3, 2, 2, 2, 513, 514, 3, 2, 2, 2, 514, 518, 3, 2, 2, 2, 515, 516, 7, 49, 2, 2, 516, 518, 10, 12, 2, 2, 517, 511, 3, 2, 2, 2, 517, 515, 3, 2, 2, 2, 518, 114, 3, 2, 2, 2, 519, 520, 5, 95, 47, 2, 520, 116, 3, 2, 2, 2, 521, 522, 5, 23, 11, 2, 522, 523, 3, 2, 2, 2, 523, 524, 8, 58, 4, 2, 524, 118, 3, 2, 2, 2, 525, 526, 5, 25, 12, 2, 526, 527, 3, 2, 2, 2, 527, 528, 8, 59, 4, 2, 528, 120, 3, 2, 2, 2, 529, 530, 5, 27, 13, 2, 530, 531, 3, 2, 2, 2, 531, 532, 8, 60, 4, 2, 532, 122, 3, 2, 2, 2, 41, 2, 3, 4, 200, 204, 207, 216, 218, 229, 248, 253, 258, 260, 271, 279, 282, 284, 289, 294, 300, 307, 312, 318, 321, 329, 333, 382, 394, 413, 430, 449, 453, 458, 460, 467, 469, 508, 513, 517, 11, 7, 3, 2, 7, 4, 2, 2, 3, 2, 6, 2, 2, 7, 2, 2, 9, 15, 2, 9, 26, 2, 9, 22, 2, 9, 21, 2] \ No newline at end of file diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens b/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens index b39004ce4ce3..c2dafff2f222 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens @@ -16,50 +16,37 @@ INTEGER_LITERAL=15 DECIMAL_LITERAL=16 BY=17 AND=18 -ASC=19 -ASSIGN=20 -COMMA=21 -DESC=22 -DOT=23 -FALSE=24 -FIRST=25 -LAST=26 -LP=27 -OPENING_BRACKET=28 -CLOSING_BRACKET=29 -NOT=30 -NULL=31 -NULLS=32 -OR=33 -RP=34 -TRUE=35 -EQ=36 -NEQ=37 -LT=38 -LTE=39 -GT=40 -GTE=41 -PLUS=42 -MINUS=43 -ASTERISK=44 -SLASH=45 -PERCENT=46 -ROUND_FUNCTION_MATH=47 -AVG_FUNCTION_MATH=48 -SUM_FUNCTION_MATH=49 -MIN_FUNCTION_MATH=50 -MAX_FUNCTION_MATH=51 -UNQUOTED_IDENTIFIER=52 -QUOTED_IDENTIFIER=53 -EXPR_LINE_COMMENT=54 -EXPR_MULTILINE_COMMENT=55 -EXPR_WS=56 -SRC_UNQUOTED_IDENTIFIER=57 -SRC_QUOTED_IDENTIFIER=58 -SRC_LINE_COMMENT=59 -SRC_MULTILINE_COMMENT=60 -SRC_WS=61 -UNKNOWN_CMD=62 +ASSIGN=19 +COMMA=20 +DOT=21 +LP=22 +OPENING_BRACKET=23 +CLOSING_BRACKET=24 +NOT=25 +NULL=26 +OR=27 +RP=28 +BOOLEAN_VALUE=29 +COMPARISON_OPERATOR=30 +PLUS=31 +MINUS=32 +ASTERISK=33 +SLASH=34 +PERCENT=35 +ORDERING=36 +NULLS_ORDERING=37 +NULLS_ORDERING_DIRECTION=38 +UNARY_FUNCTION=39 +UNQUOTED_IDENTIFIER=40 +QUOTED_IDENTIFIER=41 +EXPR_LINE_COMMENT=42 +EXPR_MULTILINE_COMMENT=43 +EXPR_WS=44 +SRC_UNQUOTED_IDENTIFIER=45 +SRC_QUOTED_IDENTIFIER=46 +SRC_LINE_COMMENT=47 +SRC_MULTILINE_COMMENT=48 +SRC_WS=49 'eval'=1 'explain'=2 'from'=3 @@ -71,34 +58,17 @@ UNKNOWN_CMD=62 'project'=9 'by'=17 'and'=18 -'asc'=19 -'desc'=22 -'.'=23 -'false'=24 -'first'=25 -'last'=26 -'('=27 -'['=28 -']'=29 -'not'=30 -'null'=31 -'nulls'=32 -'or'=33 -')'=34 -'true'=35 -'=='=36 -'!='=37 -'<'=38 -'<='=39 -'>'=40 -'>='=41 -'+'=42 -'-'=43 -'*'=44 -'/'=45 -'%'=46 -'round'=47 -'avg'=48 -'sum'=49 -'min'=50 -'max'=51 +'.'=21 +'('=22 +'['=23 +']'=24 +'not'=25 +'null'=26 +'or'=27 +')'=28 +'+'=31 +'-'=32 +'*'=33 +'/'=34 +'%'=35 +'nulls'=37 diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts b/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts index de10e76f9c72..064b2fe2c02d 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts @@ -35,50 +35,37 @@ export class esql_lexer extends Lexer { public static readonly DECIMAL_LITERAL = 16; public static readonly BY = 17; public static readonly AND = 18; - public static readonly ASC = 19; - public static readonly ASSIGN = 20; - public static readonly COMMA = 21; - public static readonly DESC = 22; - public static readonly DOT = 23; - public static readonly FALSE = 24; - public static readonly FIRST = 25; - public static readonly LAST = 26; - public static readonly LP = 27; - public static readonly OPENING_BRACKET = 28; - public static readonly CLOSING_BRACKET = 29; - public static readonly NOT = 30; - public static readonly NULL = 31; - public static readonly NULLS = 32; - public static readonly OR = 33; - public static readonly RP = 34; - public static readonly TRUE = 35; - public static readonly EQ = 36; - public static readonly NEQ = 37; - public static readonly LT = 38; - public static readonly LTE = 39; - public static readonly GT = 40; - public static readonly GTE = 41; - public static readonly PLUS = 42; - public static readonly MINUS = 43; - public static readonly ASTERISK = 44; - public static readonly SLASH = 45; - public static readonly PERCENT = 46; - public static readonly ROUND_FUNCTION_MATH = 47; - public static readonly AVG_FUNCTION_MATH = 48; - public static readonly SUM_FUNCTION_MATH = 49; - public static readonly MIN_FUNCTION_MATH = 50; - public static readonly MAX_FUNCTION_MATH = 51; - public static readonly UNQUOTED_IDENTIFIER = 52; - public static readonly QUOTED_IDENTIFIER = 53; - public static readonly EXPR_LINE_COMMENT = 54; - public static readonly EXPR_MULTILINE_COMMENT = 55; - public static readonly EXPR_WS = 56; - public static readonly SRC_UNQUOTED_IDENTIFIER = 57; - public static readonly SRC_QUOTED_IDENTIFIER = 58; - public static readonly SRC_LINE_COMMENT = 59; - public static readonly SRC_MULTILINE_COMMENT = 60; - public static readonly SRC_WS = 61; - public static readonly UNKNOWN_CMD = 62; + public static readonly ASSIGN = 19; + public static readonly COMMA = 20; + public static readonly DOT = 21; + public static readonly LP = 22; + public static readonly OPENING_BRACKET = 23; + public static readonly CLOSING_BRACKET = 24; + public static readonly NOT = 25; + public static readonly NULL = 26; + public static readonly OR = 27; + public static readonly RP = 28; + public static readonly BOOLEAN_VALUE = 29; + public static readonly COMPARISON_OPERATOR = 30; + public static readonly PLUS = 31; + public static readonly MINUS = 32; + public static readonly ASTERISK = 33; + public static readonly SLASH = 34; + public static readonly PERCENT = 35; + public static readonly ORDERING = 36; + public static readonly NULLS_ORDERING = 37; + public static readonly NULLS_ORDERING_DIRECTION = 38; + public static readonly UNARY_FUNCTION = 39; + public static readonly UNQUOTED_IDENTIFIER = 40; + public static readonly QUOTED_IDENTIFIER = 41; + public static readonly EXPR_LINE_COMMENT = 42; + public static readonly EXPR_MULTILINE_COMMENT = 43; + public static readonly EXPR_WS = 44; + public static readonly SRC_UNQUOTED_IDENTIFIER = 45; + public static readonly SRC_QUOTED_IDENTIFIER = 46; + public static readonly SRC_LINE_COMMENT = 47; + public static readonly SRC_MULTILINE_COMMENT = 48; + public static readonly SRC_WS = 49; public static readonly EXPRESSION = 1; public static readonly SOURCE_IDENTIFIERS = 2; @@ -96,37 +83,33 @@ export class esql_lexer extends Lexer { "EVAL", "EXPLAIN", "FROM", "ROW", "STATS", "WHERE", "SORT", "LIMIT", "PROJECT", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", "UNESCAPED_CHARS", "EXPONENT", "STRING", "INTEGER_LITERAL", - "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", "COMMA", "DESC", "DOT", - "FALSE", "FIRST", "LAST", "LP", "OPENING_BRACKET", "CLOSING_BRACKET", - "NOT", "NULL", "NULLS", "OR", "RP", "TRUE", "EQ", "NEQ", "LT", "LTE", - "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "ROUND_FUNCTION_MATH", - "AVG_FUNCTION_MATH", "SUM_FUNCTION_MATH", "MIN_FUNCTION_MATH", "MAX_FUNCTION_MATH", - "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", - "EXPR_WS", "SRC_PIPE", "SRC_CLOSING_BRACKET", "SRC_COMMA", "SRC_ASSIGN", - "SRC_UNQUOTED_IDENTIFIER", "SRC_UNQUOTED_IDENTIFIER_PART", "SRC_QUOTED_IDENTIFIER", - "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", "SRC_WS", "UNKNOWN_CMD", + "DECIMAL_LITERAL", "BY", "AND", "ASSIGN", "COMMA", "DOT", "LP", "OPENING_BRACKET", + "CLOSING_BRACKET", "NOT", "NULL", "OR", "RP", "BOOLEAN_VALUE", "COMPARISON_OPERATOR", + "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", "ORDERING", "NULLS_ORDERING", + "NULLS_ORDERING_DIRECTION", "UNARY_FUNCTION", "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", + "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "SRC_PIPE", + "SRC_CLOSING_BRACKET", "SRC_COMMA", "SRC_ASSIGN", "SRC_UNQUOTED_IDENTIFIER", + "SRC_UNQUOTED_IDENTIFIER_PART", "SRC_QUOTED_IDENTIFIER", "SRC_LINE_COMMENT", + "SRC_MULTILINE_COMMENT", "SRC_WS", ]; private static readonly _LITERAL_NAMES: Array = [ undefined, "'eval'", "'explain'", "'from'", "'row'", "'stats'", "'where'", "'sort'", "'limit'", "'project'", undefined, undefined, undefined, undefined, - undefined, undefined, undefined, "'by'", "'and'", "'asc'", undefined, - undefined, "'desc'", "'.'", "'false'", "'first'", "'last'", "'('", "'['", - "']'", "'not'", "'null'", "'nulls'", "'or'", "')'", "'true'", "'=='", - "'!='", "'<'", "'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", - "'round'", "'avg'", "'sum'", "'min'", "'max'", + undefined, undefined, undefined, "'by'", "'and'", undefined, undefined, + "'.'", "'('", "'['", "']'", "'not'", "'null'", "'or'", "')'", undefined, + undefined, "'+'", "'-'", "'*'", "'/'", "'%'", undefined, "'nulls'", ]; private static readonly _SYMBOLIC_NAMES: Array = [ undefined, "EVAL", "EXPLAIN", "FROM", "ROW", "STATS", "WHERE", "SORT", "LIMIT", "PROJECT", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "PIPE", - "STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", - "COMMA", "DESC", "DOT", "FALSE", "FIRST", "LAST", "LP", "OPENING_BRACKET", - "CLOSING_BRACKET", "NOT", "NULL", "NULLS", "OR", "RP", "TRUE", "EQ", "NEQ", - "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "ROUND_FUNCTION_MATH", "AVG_FUNCTION_MATH", "SUM_FUNCTION_MATH", "MIN_FUNCTION_MATH", - "MAX_FUNCTION_MATH", "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", + "STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASSIGN", + "COMMA", "DOT", "LP", "OPENING_BRACKET", "CLOSING_BRACKET", "NOT", "NULL", + "OR", "RP", "BOOLEAN_VALUE", "COMPARISON_OPERATOR", "PLUS", "MINUS", "ASTERISK", + "SLASH", "PERCENT", "ORDERING", "NULLS_ORDERING", "NULLS_ORDERING_DIRECTION", + "UNARY_FUNCTION", "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "SRC_UNQUOTED_IDENTIFIER", "SRC_QUOTED_IDENTIFIER", - "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", "SRC_WS", "UNKNOWN_CMD", + "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", "SRC_WS", ]; public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(esql_lexer._LITERAL_NAMES, esql_lexer._SYMBOLIC_NAMES, []); @@ -158,9 +141,8 @@ export class esql_lexer extends Lexer { // @Override public get modeNames(): string[] { return esql_lexer.modeNames; } - private static readonly _serializedATNSegments: number = 2; - private static readonly _serializedATNSegment0: string = - "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x02@\u023D\b\x01" + + public static readonly _serializedATN: string = + "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x023\u0215\b\x01" + "\b\x01\b\x01\x04\x02\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04" + "\x06\t\x06\x04\x07\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f" + "\t\f\x04\r\t\r\x04\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11" + @@ -170,280 +152,255 @@ export class esql_lexer extends Lexer { "\t!\x04\"\t\"\x04#\t#\x04$\t$\x04%\t%\x04&\t&\x04\'\t\'\x04(\t(\x04)\t" + ")\x04*\t*\x04+\t+\x04,\t,\x04-\t-\x04.\t.\x04/\t/\x040\t0\x041\t1\x04" + "2\t2\x043\t3\x044\t4\x045\t5\x046\t6\x047\t7\x048\t8\x049\t9\x04:\t:\x04" + - ";\t;\x04<\t<\x04=\t=\x04>\t>\x04?\t?\x04@\t@\x04A\tA\x04B\tB\x04C\tC\x04" + - "D\tD\x04E\tE\x04F\tF\x04G\tG\x04H\tH\x04I\tI\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03" + - "\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x07\x03" + - "\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\b\x03\b\x03\b" + - "\x03\b\x03\b\x03\b\x03\b\x03\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03" + - "\t\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\v\x03" + - "\v\x03\v\x03\v\x07\v\xE1\n\v\f\v\x0E\v\xE4\v\v\x03\v\x05\v\xE7\n\v\x03" + - "\v\x05\v\xEA\n\v\x03\v\x03\v\x03\f\x03\f\x03\f\x03\f\x03\f\x07\f\xF3\n" + - "\f\f\f\x0E\f\xF6\v\f\x03\f\x03\f\x03\f\x03\f\x03\f\x03\r\x06\r\xFE\n\r" + - "\r\r\x0E\r\xFF\x03\r\x03\r\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03\x0F\x03" + - "\x0F\x03\x10\x03\x10\x03\x11\x03\x11\x03\x11\x03\x12\x03\x12\x03\x13\x03" + - "\x13\x05\x13\u0113\n\x13\x03\x13\x06\x13\u0116\n\x13\r\x13\x0E\x13\u0117" + - "\x03\x14\x03\x14\x03\x14\x07\x14\u011D\n\x14\f\x14\x0E\x14\u0120\v\x14" + - "\x03\x14\x03\x14\x03\x14\x03\x14\x03\x14\x03\x14\x07\x14\u0128\n\x14\f" + - "\x14\x0E\x14\u012B\v\x14\x03\x14\x03\x14\x03\x14\x03\x14\x03\x14\x05\x14" + - "\u0132\n\x14\x03\x14\x05\x14\u0135\n\x14\x05\x14\u0137\n\x14\x03\x15\x06" + - "\x15\u013A\n\x15\r\x15\x0E\x15\u013B\x03\x16\x06\x16\u013F\n\x16\r\x16" + - "\x0E\x16\u0140\x03\x16\x03\x16\x07\x16\u0145\n\x16\f\x16\x0E\x16\u0148" + - "\v\x16\x03\x16\x03\x16\x06\x16\u014C\n\x16\r\x16\x0E\x16\u014D\x03\x16" + - "\x06\x16\u0151\n\x16\r\x16\x0E\x16\u0152\x03\x16\x03\x16\x07\x16\u0157" + - "\n\x16\f\x16\x0E\x16\u015A\v\x16\x05\x16\u015C\n\x16\x03\x16\x03\x16\x03" + - "\x16\x03\x16\x06\x16\u0162\n\x16\r\x16\x0E\x16\u0163\x03\x16\x03\x16\x05" + - "\x16\u0168\n\x16\x03\x17\x03\x17\x03\x17\x03\x18\x03\x18\x03\x18\x03\x18" + - "\x03\x19\x03\x19\x03\x19\x03\x19\x03\x1A\x03\x1A\x03\x1B\x03\x1B\x03\x1C" + - "\x03\x1C\x03\x1C\x03\x1C\x03\x1C\x03\x1D\x03\x1D\x03\x1E\x03\x1E\x03\x1E" + - "\x03\x1E\x03\x1E\x03\x1E\x03\x1F\x03\x1F\x03\x1F\x03\x1F\x03\x1F\x03\x1F" + - "\x03 \x03 \x03 \x03 \x03 \x03!\x03!\x03\"\x03\"\x03\"\x03\"\x03#\x03#" + - "\x03#\x03#\x03#\x03$\x03$\x03$\x03$\x03%\x03%\x03%\x03%\x03%\x03&\x03" + - "&\x03&\x03&\x03&\x03&\x03\'\x03\'\x03\'\x03(\x03(\x03)\x03)\x03)\x03)" + - "\x03)\x03*\x03*\x03*\x03+\x03+\x03+\x03,\x03,\x03-\x03-\x03-\x03.\x03" + - ".\x03/\x03/\x03/\x030\x030\x031\x031\x032\x032\x033\x033\x034\x034\x03" + - "5\x035\x035\x035\x035\x035\x036\x036\x036\x036\x037\x037\x037\x037\x03" + - "8\x038\x038\x038\x039\x039\x039\x039\x03:\x03:\x05:\u01E7\n:\x03:\x03" + - ":\x03:\x07:\u01EC\n:\f:\x0E:\u01EF\v:\x03;\x03;\x03;\x03;\x07;\u01F5\n" + - ";\f;\x0E;\u01F8\v;\x03;\x03;\x03<\x03<\x03<\x03<\x03=\x03=\x03=\x03=\x03" + - ">\x03>\x03>\x03>\x03?\x03?\x03?\x03?\x03?\x03@\x03@\x03@\x03@\x03@\x03" + - "@\x03A\x03A\x03A\x03A\x03B\x03B\x03B\x03B\x03C\x06C\u021C\nC\rC\x0EC\u021D" + - "\x03D\x06D\u0221\nD\rD\x0ED\u0222\x03D\x03D\x05D\u0227\nD\x03E\x03E\x03" + - "F\x03F\x03F\x03F\x03G\x03G\x03G\x03G\x03H\x03H\x03H\x03H\x03I\x06I\u0238" + - "\nI\rI\x0EI\u0239\x03I\x03I\x04\xF4\u0129\x02\x02J\x05\x02\x03\x07\x02" + - "\x04\t\x02\x05\v\x02\x06\r\x02\x07\x0F\x02\b\x11\x02\t\x13\x02\n\x15\x02" + - "\v\x17\x02\f\x19\x02\r\x1B\x02\x0E\x1D\x02\x0F\x1F\x02\x02!\x02\x02#\x02" + - "\x02%\x02\x02\'\x02\x02)\x02\x10+\x02\x11-\x02\x12/\x02\x131\x02\x143" + - "\x02\x155\x02\x167\x02\x179\x02\x18;\x02\x19=\x02\x1A?\x02\x1BA\x02\x1C" + - "C\x02\x1DE\x02\x1EG\x02\x1FI\x02 K\x02!M\x02\"O\x02#Q\x02$S\x02%U\x02" + - "&W\x02\'Y\x02([\x02)]\x02*_\x02+a\x02,c\x02-e\x02.g\x02/i\x020k\x021m" + - "\x022o\x023q\x024s\x025u\x026w\x027y\x028{\x029}\x02:\x7F\x02\x02\x81" + - "\x02\x02\x83\x02\x02\x85\x02\x02\x87\x02;\x89\x02\x02\x8B\x02<\x8D\x02" + - "=\x8F\x02>\x91\x02?\x93\x02@\x05\x02\x03\x04\x0E\x04\x02\f\f\x0F\x0F\x05" + - "\x02\v\f\x0F\x0F\"\"\x03\x022;\x04\x02C\\c|\x07\x02$$^^ppttvv\x06\x02" + - "\f\f\x0F\x0F$$^^\x04\x02GGgg\x04\x02--//\x03\x02bb\f\x02\v\f\x0F\x0F\"" + - "\"..11??]]__bb~~\x04\x02,,11\b\x02\v\f\x0F\x0F\"\"11]]__\x02\u0257\x02" + - "\x05\x03\x02\x02\x02\x02\x07\x03\x02\x02\x02\x02\t\x03\x02\x02\x02\x02" + - "\v\x03\x02\x02\x02\x02\r\x03\x02\x02\x02\x02\x0F\x03\x02\x02\x02\x02\x11" + - "\x03\x02\x02\x02\x02\x13\x03\x02\x02\x02\x02\x15\x03\x02\x02\x02\x02\x17" + - "\x03\x02\x02\x02\x02\x19\x03\x02\x02\x02\x02\x1B\x03\x02\x02\x02\x03\x1D" + - "\x03\x02\x02\x02\x03)\x03\x02\x02\x02\x03+\x03\x02\x02\x02\x03-\x03\x02" + - "\x02\x02\x03/\x03\x02\x02\x02\x031\x03\x02\x02\x02\x033\x03\x02\x02\x02" + - "\x035\x03\x02\x02\x02\x037\x03\x02\x02\x02\x039\x03\x02\x02\x02\x03;\x03" + - "\x02\x02\x02\x03=\x03\x02\x02\x02\x03?\x03\x02\x02\x02\x03A\x03\x02\x02" + - "\x02\x03C\x03\x02\x02\x02\x03E\x03\x02\x02\x02\x03G\x03\x02\x02\x02\x03" + - "I\x03\x02\x02\x02\x03K\x03\x02\x02\x02\x03M\x03\x02\x02\x02\x03O\x03\x02" + - "\x02\x02\x03Q\x03\x02\x02\x02\x03S\x03\x02\x02\x02\x03U\x03\x02\x02\x02" + - "\x03W\x03\x02\x02\x02\x03Y\x03\x02\x02\x02\x03[\x03\x02\x02\x02\x03]\x03" + - "\x02\x02\x02\x03_\x03\x02\x02\x02\x03a\x03\x02\x02\x02\x03c\x03\x02\x02" + - "\x02\x03e\x03\x02\x02\x02\x03g\x03\x02\x02\x02\x03i\x03\x02\x02\x02\x03" + - "k\x03\x02\x02\x02\x03m\x03\x02\x02\x02\x03o\x03\x02\x02\x02\x03q\x03\x02" + - "\x02\x02\x03s\x03\x02\x02\x02\x03u\x03\x02\x02\x02\x03w\x03\x02\x02\x02" + - "\x03y\x03\x02\x02\x02\x03{\x03\x02\x02\x02\x03}\x03\x02\x02\x02\x04\x7F" + - "\x03\x02\x02\x02\x04\x81\x03\x02\x02\x02\x04\x83\x03\x02\x02\x02\x04\x85" + - "\x03\x02\x02\x02\x04\x87\x03\x02\x02\x02\x04\x8B\x03\x02\x02\x02\x04\x8D" + - "\x03\x02\x02\x02\x04\x8F\x03\x02\x02\x02\x04\x91\x03\x02\x02\x02\x04\x93" + - "\x03\x02\x02\x02\x05\x95\x03\x02\x02\x02\x07\x9C\x03\x02\x02\x02\t\xA6" + - "\x03\x02\x02\x02\v\xAD\x03\x02\x02\x02\r\xB3\x03\x02\x02\x02\x0F\xBB\x03" + - "\x02\x02\x02\x11\xC3\x03\x02\x02\x02\x13\xCA\x03\x02\x02\x02\x15\xD2\x03" + - "\x02\x02\x02\x17\xDC\x03\x02\x02\x02\x19\xED\x03\x02\x02\x02\x1B\xFD\x03" + - "\x02\x02\x02\x1D\u0103\x03\x02\x02\x02\x1F\u0107\x03\x02\x02\x02!\u0109" + - "\x03\x02\x02\x02#\u010B\x03\x02\x02\x02%\u010E\x03\x02\x02\x02\'\u0110" + - "\x03\x02\x02\x02)\u0136\x03\x02\x02\x02+\u0139\x03\x02\x02\x02-\u0167" + - "\x03\x02\x02\x02/\u0169\x03\x02\x02\x021\u016C\x03\x02\x02\x023\u0170" + - "\x03\x02\x02\x025\u0174\x03\x02\x02\x027\u0176\x03\x02\x02\x029\u0178" + - "\x03\x02\x02\x02;\u017D\x03\x02\x02\x02=\u017F\x03\x02\x02\x02?\u0185" + - "\x03\x02\x02\x02A\u018B\x03\x02\x02\x02C\u0190\x03\x02\x02\x02E\u0192" + - "\x03\x02\x02\x02G\u0196\x03\x02\x02\x02I\u019B\x03\x02\x02\x02K\u019F" + - "\x03\x02\x02\x02M\u01A4\x03\x02\x02\x02O\u01AA\x03\x02\x02\x02Q\u01AD" + - "\x03\x02\x02\x02S\u01AF\x03\x02\x02\x02U\u01B4\x03\x02\x02\x02W\u01B7" + - "\x03\x02\x02\x02Y\u01BA\x03\x02\x02\x02[\u01BC\x03\x02\x02\x02]\u01BF" + - "\x03\x02\x02\x02_\u01C1\x03\x02\x02\x02a\u01C4\x03\x02\x02\x02c\u01C6" + - "\x03\x02\x02\x02e\u01C8\x03\x02\x02\x02g\u01CA\x03\x02\x02\x02i\u01CC" + - "\x03\x02\x02\x02k\u01CE\x03\x02\x02\x02m\u01D4\x03\x02\x02\x02o\u01D8" + - "\x03\x02\x02\x02q\u01DC\x03\x02\x02\x02s\u01E0\x03\x02\x02\x02u\u01E6" + - "\x03\x02\x02\x02w\u01F0\x03\x02\x02\x02y\u01FB\x03\x02\x02\x02{\u01FF" + - "\x03\x02\x02\x02}\u0203\x03\x02\x02\x02\x7F\u0207\x03\x02\x02\x02\x81" + - "\u020C\x03\x02\x02\x02\x83\u0212\x03\x02\x02\x02\x85\u0216\x03\x02\x02" + - "\x02\x87\u021B\x03\x02\x02\x02\x89\u0226\x03\x02\x02\x02\x8B\u0228\x03" + - "\x02\x02\x02\x8D\u022A\x03\x02\x02\x02\x8F\u022E\x03\x02\x02\x02\x91\u0232" + - "\x03\x02\x02\x02\x93\u0237\x03\x02\x02\x02\x95\x96\x07g\x02\x02\x96\x97" + - "\x07x\x02\x02\x97\x98\x07c\x02\x02\x98\x99\x07n\x02\x02\x99\x9A\x03\x02" + - "\x02\x02\x9A\x9B\b\x02\x02\x02\x9B\x06\x03\x02\x02\x02\x9C\x9D\x07g\x02" + - "\x02\x9D\x9E\x07z\x02\x02\x9E\x9F\x07r\x02\x02\x9F\xA0\x07n\x02\x02\xA0" + - "\xA1\x07c\x02\x02\xA1\xA2\x07k\x02\x02\xA2\xA3\x07p\x02\x02\xA3\xA4\x03" + - "\x02\x02\x02\xA4\xA5\b\x03\x02\x02\xA5\b\x03\x02\x02\x02\xA6\xA7\x07h" + - "\x02\x02\xA7\xA8\x07t\x02\x02\xA8\xA9\x07q\x02\x02\xA9\xAA\x07o\x02\x02" + - "\xAA\xAB\x03\x02\x02\x02\xAB\xAC\b\x04\x03\x02\xAC\n\x03\x02\x02\x02\xAD" + - "\xAE\x07t\x02\x02\xAE\xAF\x07q\x02\x02\xAF\xB0\x07y\x02\x02\xB0\xB1\x03" + - "\x02\x02\x02\xB1\xB2\b\x05\x02\x02\xB2\f\x03\x02\x02\x02\xB3\xB4\x07u" + - "\x02\x02\xB4\xB5\x07v\x02\x02\xB5\xB6\x07c\x02\x02\xB6\xB7\x07v\x02\x02" + - "\xB7\xB8\x07u\x02\x02\xB8\xB9\x03\x02\x02\x02\xB9\xBA\b\x06\x02\x02\xBA" + - "\x0E\x03\x02\x02\x02\xBB\xBC\x07y\x02\x02\xBC\xBD\x07j\x02\x02\xBD\xBE" + - "\x07g\x02\x02\xBE\xBF\x07t\x02\x02\xBF\xC0\x07g\x02\x02\xC0\xC1\x03\x02" + - "\x02\x02\xC1\xC2\b\x07\x02\x02\xC2\x10\x03\x02\x02\x02\xC3\xC4\x07u\x02" + - "\x02\xC4\xC5\x07q\x02\x02\xC5\xC6\x07t\x02\x02\xC6\xC7\x07v\x02\x02\xC7" + - "\xC8\x03\x02\x02\x02\xC8\xC9\b\b\x02\x02\xC9\x12\x03\x02\x02\x02\xCA\xCB" + - "\x07n\x02\x02\xCB\xCC\x07k\x02\x02\xCC\xCD\x07o\x02\x02\xCD\xCE\x07k\x02" + - "\x02\xCE\xCF\x07v\x02\x02\xCF\xD0\x03\x02\x02\x02\xD0\xD1\b\t\x02\x02" + - "\xD1\x14\x03\x02\x02\x02\xD2\xD3\x07r\x02\x02\xD3\xD4\x07t\x02\x02\xD4" + - "\xD5\x07q\x02\x02\xD5\xD6\x07l\x02\x02\xD6\xD7\x07g\x02\x02\xD7\xD8\x07" + - "e\x02\x02\xD8\xD9\x07v\x02\x02\xD9\xDA\x03\x02\x02\x02\xDA\xDB\b\n\x03" + - "\x02\xDB\x16\x03\x02\x02\x02\xDC\xDD\x071\x02\x02\xDD\xDE\x071\x02\x02" + - "\xDE\xE2\x03\x02\x02\x02\xDF\xE1\n\x02\x02\x02\xE0\xDF\x03\x02\x02\x02" + - "\xE1\xE4\x03\x02\x02\x02\xE2\xE0\x03\x02\x02\x02\xE2\xE3\x03\x02\x02\x02" + - "\xE3\xE6\x03\x02\x02\x02\xE4\xE2\x03\x02\x02\x02\xE5\xE7\x07\x0F\x02\x02" + - "\xE6\xE5\x03\x02\x02\x02\xE6\xE7\x03\x02\x02\x02\xE7\xE9\x03\x02\x02\x02" + - "\xE8\xEA\x07\f\x02\x02\xE9\xE8\x03\x02\x02\x02\xE9\xEA\x03\x02\x02\x02" + - "\xEA\xEB\x03\x02\x02\x02\xEB\xEC\b\v\x04\x02\xEC\x18\x03\x02\x02\x02\xED" + - "\xEE\x071\x02\x02\xEE\xEF\x07,\x02\x02\xEF\xF4\x03\x02\x02\x02\xF0\xF3" + - "\x05\x19\f\x02\xF1\xF3\v\x02\x02\x02\xF2\xF0\x03\x02\x02\x02\xF2\xF1\x03" + - "\x02\x02\x02\xF3\xF6\x03\x02\x02\x02\xF4\xF5\x03\x02\x02\x02\xF4\xF2\x03" + - "\x02\x02\x02\xF5\xF7\x03\x02\x02\x02\xF6\xF4\x03\x02\x02\x02\xF7\xF8\x07" + - ",\x02\x02\xF8\xF9\x071\x02\x02\xF9\xFA\x03\x02\x02\x02\xFA\xFB\b\f\x04" + - "\x02\xFB\x1A\x03\x02\x02\x02\xFC\xFE\t\x03\x02\x02\xFD\xFC\x03\x02\x02" + - "\x02\xFE\xFF\x03\x02\x02\x02\xFF\xFD\x03\x02\x02\x02\xFF\u0100\x03\x02" + - "\x02\x02\u0100\u0101\x03\x02\x02\x02\u0101\u0102\b\r\x04\x02\u0102\x1C" + - "\x03\x02\x02\x02\u0103\u0104\x07~\x02\x02\u0104\u0105\x03\x02\x02\x02" + - "\u0105\u0106\b\x0E\x05\x02\u0106\x1E\x03\x02\x02\x02\u0107\u0108\t\x04" + - "\x02\x02\u0108 \x03\x02\x02\x02\u0109\u010A\t\x05\x02\x02\u010A\"\x03" + - "\x02\x02\x02\u010B\u010C\x07^\x02\x02\u010C\u010D\t\x06\x02\x02\u010D" + - "$\x03\x02\x02\x02\u010E\u010F\n\x07\x02\x02\u010F&\x03\x02\x02\x02\u0110" + - "\u0112\t\b\x02\x02\u0111\u0113\t\t\x02\x02\u0112\u0111\x03\x02\x02\x02" + - "\u0112\u0113\x03\x02\x02\x02\u0113\u0115\x03\x02\x02\x02\u0114\u0116\x05" + - "\x1F\x0F\x02\u0115\u0114\x03\x02\x02\x02\u0116\u0117\x03\x02\x02\x02\u0117" + - "\u0115\x03\x02\x02\x02\u0117\u0118\x03\x02\x02\x02\u0118(\x03\x02\x02" + - "\x02\u0119\u011E\x07$\x02\x02\u011A\u011D\x05#\x11\x02\u011B\u011D\x05" + - "%\x12\x02\u011C\u011A\x03\x02\x02\x02\u011C\u011B\x03\x02\x02\x02\u011D" + - "\u0120\x03\x02\x02\x02\u011E\u011C\x03\x02\x02\x02\u011E\u011F\x03\x02" + - "\x02\x02\u011F\u0121\x03\x02\x02\x02\u0120\u011E\x03\x02\x02\x02\u0121" + - "\u0137\x07$\x02\x02\u0122\u0123\x07$\x02\x02\u0123\u0124\x07$\x02\x02" + - "\u0124\u0125\x07$\x02\x02\u0125\u0129\x03\x02\x02\x02\u0126\u0128\n\x02" + - "\x02\x02\u0127\u0126\x03\x02\x02\x02\u0128\u012B\x03\x02\x02\x02\u0129" + - "\u012A\x03\x02\x02\x02\u0129\u0127\x03\x02\x02\x02\u012A\u012C\x03\x02" + - "\x02\x02\u012B\u0129\x03\x02\x02\x02\u012C\u012D\x07$\x02\x02\u012D\u012E" + - "\x07$\x02\x02\u012E\u012F\x07$\x02\x02\u012F\u0131\x03\x02\x02\x02\u0130" + - "\u0132\x07$\x02\x02\u0131\u0130\x03\x02\x02\x02\u0131\u0132\x03\x02\x02" + - "\x02\u0132\u0134\x03\x02\x02\x02\u0133\u0135\x07$\x02\x02\u0134\u0133" + - "\x03\x02\x02\x02\u0134\u0135\x03\x02\x02\x02\u0135\u0137\x03\x02\x02\x02" + - "\u0136\u0119\x03\x02\x02\x02\u0136\u0122\x03\x02\x02\x02\u0137*\x03\x02" + - "\x02\x02\u0138\u013A\x05\x1F\x0F\x02\u0139\u0138\x03\x02\x02\x02\u013A" + - "\u013B\x03\x02\x02\x02\u013B\u0139\x03\x02\x02\x02\u013B\u013C\x03\x02" + - "\x02\x02\u013C,\x03\x02\x02\x02\u013D\u013F\x05\x1F\x0F\x02\u013E\u013D" + - "\x03\x02\x02\x02\u013F\u0140\x03\x02\x02\x02\u0140\u013E\x03\x02\x02\x02" + - "\u0140\u0141\x03\x02\x02\x02\u0141\u0142\x03\x02\x02\x02\u0142\u0146\x05" + - ";\x1D\x02\u0143\u0145\x05\x1F\x0F\x02\u0144\u0143\x03\x02\x02\x02\u0145" + - "\u0148\x03\x02\x02\x02\u0146\u0144\x03\x02\x02\x02\u0146\u0147\x03\x02" + - "\x02\x02\u0147\u0168\x03\x02\x02\x02\u0148\u0146\x03\x02\x02\x02\u0149" + - "\u014B\x05;\x1D\x02\u014A\u014C\x05\x1F\x0F\x02\u014B\u014A\x03\x02\x02" + - "\x02\u014C\u014D\x03\x02\x02\x02\u014D\u014B\x03\x02\x02\x02\u014D\u014E" + - "\x03\x02\x02\x02\u014E\u0168\x03\x02\x02\x02\u014F\u0151\x05\x1F\x0F\x02" + - "\u0150\u014F\x03\x02\x02\x02\u0151\u0152\x03\x02\x02\x02\u0152\u0150\x03" + - "\x02\x02\x02\u0152\u0153\x03\x02\x02\x02\u0153\u015B\x03\x02\x02\x02\u0154" + - "\u0158\x05;\x1D\x02\u0155\u0157\x05\x1F\x0F\x02\u0156\u0155\x03\x02\x02" + - "\x02\u0157\u015A\x03\x02\x02\x02\u0158\u0156\x03\x02\x02\x02\u0158\u0159" + - "\x03\x02\x02\x02\u0159\u015C\x03\x02\x02\x02\u015A\u0158\x03\x02\x02\x02" + - "\u015B\u0154\x03\x02\x02\x02\u015B\u015C\x03\x02\x02\x02\u015C\u015D\x03" + - "\x02\x02\x02\u015D\u015E\x05\'\x13\x02\u015E\u0168\x03\x02\x02\x02\u015F" + - "\u0161\x05;\x1D\x02\u0160\u0162\x05\x1F\x0F\x02\u0161\u0160\x03\x02\x02" + - "\x02\u0162\u0163\x03\x02\x02\x02\u0163\u0161\x03\x02\x02\x02\u0163\u0164" + - "\x03\x02\x02\x02\u0164\u0165\x03\x02\x02\x02\u0165\u0166\x05\'\x13\x02" + - "\u0166\u0168\x03\x02\x02\x02\u0167\u013E\x03\x02\x02\x02\u0167\u0149\x03" + - "\x02\x02\x02\u0167\u0150\x03\x02\x02\x02\u0167\u015F\x03\x02\x02\x02\u0168" + - ".\x03\x02\x02\x02\u0169\u016A\x07d\x02\x02\u016A\u016B\x07{\x02\x02\u016B" + - "0\x03\x02\x02\x02\u016C\u016D\x07c\x02\x02\u016D\u016E\x07p\x02\x02\u016E" + - "\u016F\x07f\x02\x02\u016F2\x03\x02\x02\x02\u0170\u0171\x07c\x02\x02\u0171" + - "\u0172\x07u\x02\x02\u0172\u0173\x07e\x02\x02\u01734\x03\x02\x02\x02\u0174" + - "\u0175\x07?\x02\x02\u01756\x03\x02\x02\x02\u0176\u0177\x07.\x02\x02\u0177" + - "8\x03\x02\x02\x02\u0178\u0179\x07f\x02\x02\u0179\u017A\x07g\x02\x02\u017A" + - "\u017B\x07u\x02\x02\u017B\u017C\x07e\x02\x02\u017C:\x03\x02\x02\x02\u017D" + - "\u017E\x070\x02\x02\u017E<\x03\x02\x02\x02\u017F\u0180\x07h\x02\x02\u0180" + - "\u0181\x07c\x02\x02\u0181\u0182\x07n\x02\x02\u0182\u0183\x07u\x02\x02" + - "\u0183\u0184\x07g\x02\x02\u0184>\x03\x02\x02\x02\u0185\u0186\x07h\x02" + - "\x02\u0186\u0187\x07k\x02\x02\u0187\u0188\x07t\x02\x02\u0188\u0189\x07" + - "u\x02\x02\u0189\u018A\x07v\x02\x02\u018A@\x03\x02\x02\x02\u018B\u018C" + - "\x07n\x02\x02\u018C\u018D\x07c\x02\x02\u018D\u018E\x07u\x02\x02\u018E" + - "\u018F\x07v\x02\x02\u018FB\x03\x02\x02\x02\u0190\u0191\x07*\x02\x02\u0191" + - "D\x03\x02\x02\x02\u0192\u0193\x07]\x02\x02\u0193\u0194\x03\x02\x02\x02" + - "\u0194\u0195\b\"\x06\x02\u0195F\x03\x02\x02\x02\u0196\u0197\x07_\x02\x02" + - "\u0197\u0198\x03\x02\x02\x02\u0198\u0199\b#\x05\x02\u0199\u019A\b#\x05" + - "\x02\u019AH\x03\x02\x02\x02\u019B\u019C\x07p\x02\x02\u019C\u019D\x07q" + - "\x02\x02\u019D\u019E\x07v\x02\x02\u019EJ\x03\x02\x02\x02\u019F\u01A0\x07" + - "p\x02\x02\u01A0\u01A1\x07w\x02\x02\u01A1\u01A2\x07n\x02\x02\u01A2\u01A3" + - "\x07n\x02\x02\u01A3L\x03\x02\x02\x02\u01A4\u01A5\x07p\x02\x02\u01A5\u01A6" + - "\x07w\x02\x02\u01A6\u01A7\x07n\x02\x02\u01A7\u01A8\x07n\x02\x02\u01A8" + - "\u01A9\x07u\x02\x02\u01A9N\x03\x02\x02\x02\u01AA\u01AB\x07q\x02\x02\u01AB" + - "\u01AC\x07t\x02\x02\u01ACP\x03\x02\x02\x02\u01AD\u01AE\x07+\x02\x02\u01AE" + - "R\x03\x02\x02\x02\u01AF\u01B0\x07v\x02\x02\u01B0\u01B1\x07t\x02\x02\u01B1" + - "\u01B2\x07w\x02\x02\u01B2\u01B3\x07g\x02\x02\u01B3T\x03\x02\x02\x02\u01B4" + - "\u01B5\x07?\x02\x02\u01B5\u01B6\x07?\x02\x02\u01B6V\x03\x02\x02\x02\u01B7" + - "\u01B8\x07#\x02\x02\u01B8\u01B9\x07?\x02\x02\u01B9X\x03\x02\x02\x02\u01BA" + - "\u01BB\x07>\x02\x02\u01BBZ\x03\x02\x02\x02\u01BC\u01BD\x07>\x02\x02\u01BD" + - "\u01BE\x07?\x02\x02\u01BE\\\x03\x02\x02\x02\u01BF\u01C0\x07@\x02\x02\u01C0" + - "^\x03\x02\x02\x02\u01C1\u01C2\x07@\x02\x02\u01C2\u01C3\x07?\x02\x02\u01C3" + - "`\x03\x02\x02\x02\u01C4\u01C5\x07-\x02\x02\u01C5b\x03\x02\x02\x02\u01C6" + - "\u01C7\x07/\x02\x02\u01C7d\x03\x02\x02\x02\u01C8\u01C9\x07,\x02\x02\u01C9" + - "f\x03\x02\x02\x02\u01CA\u01CB\x071\x02\x02\u01CBh\x03\x02\x02\x02\u01CC" + - "\u01CD\x07\'\x02\x02\u01CDj\x03\x02\x02\x02\u01CE\u01CF\x07t\x02\x02\u01CF" + - "\u01D0\x07q\x02\x02\u01D0\u01D1\x07w\x02\x02\u01D1\u01D2\x07p\x02\x02" + - "\u01D2\u01D3\x07f\x02\x02\u01D3l\x03\x02\x02\x02\u01D4\u01D5\x07c\x02" + - "\x02\u01D5\u01D6\x07x\x02\x02\u01D6\u01D7\x07i\x02\x02\u01D7n\x03\x02" + - "\x02\x02\u01D8\u01D9\x07u\x02\x02\u01D9\u01DA\x07w\x02\x02\u01DA\u01DB" + - "\x07o\x02\x02\u01DBp\x03\x02\x02\x02\u01DC\u01DD\x07o\x02\x02\u01DD\u01DE" + - "\x07k\x02\x02\u01DE\u01DF\x07p\x02\x02\u01DFr\x03\x02\x02\x02\u01E0\u01E1" + - "\x07o\x02\x02\u01E1\u01E2\x07c\x02\x02\u01E2\u01E3\x07z\x02\x02\u01E3" + - "t\x03\x02\x02\x02\u01E4\u01E7\x05!\x10\x02\u01E5\u01E7\x07a\x02\x02\u01E6" + - "\u01E4\x03\x02\x02\x02\u01E6\u01E5\x03\x02\x02\x02\u01E7\u01ED\x03\x02" + - "\x02\x02\u01E8\u01EC\x05!\x10\x02\u01E9\u01EC\x05\x1F\x0F\x02\u01EA\u01EC" + - "\x07a\x02\x02\u01EB\u01E8\x03\x02\x02\x02\u01EB\u01E9\x03\x02\x02\x02" + - "\u01EB\u01EA\x03\x02\x02\x02\u01EC\u01EF\x03\x02\x02\x02\u01ED\u01EB\x03" + - "\x02\x02\x02\u01ED\u01EE\x03\x02\x02\x02\u01EEv\x03\x02\x02\x02\u01EF" + - "\u01ED\x03\x02\x02\x02\u01F0\u01F6\x07b\x02\x02\u01F1\u01F5\n\n\x02\x02" + - "\u01F2\u01F3\x07b\x02\x02\u01F3\u01F5\x07b\x02\x02\u01F4\u01F1\x03\x02" + - "\x02\x02\u01F4\u01F2\x03\x02\x02\x02\u01F5\u01F8\x03\x02\x02\x02\u01F6" + - "\u01F4\x03\x02\x02\x02\u01F6\u01F7\x03\x02\x02\x02\u01F7\u01F9\x03\x02" + - "\x02\x02\u01F8\u01F6\x03\x02\x02\x02\u01F9\u01FA\x07b\x02\x02\u01FAx\x03" + - "\x02\x02\x02\u01FB\u01FC\x05\x17\v\x02\u01FC\u01FD\x03\x02\x02\x02\u01FD" + - "\u01FE\b<\x04\x02\u01FEz\x03\x02\x02\x02\u01FF\u0200\x05\x19\f\x02\u0200" + - "\u0201\x03\x02\x02\x02\u0201\u0202\b=\x04\x02\u0202|\x03\x02\x02\x02\u0203" + - "\u0204\x05\x1B\r\x02\u0204\u0205\x03\x02\x02\x02\u0205\u0206\b>\x04\x02" + - "\u0206~\x03\x02\x02\x02\u0207\u0208\x07~\x02\x02\u0208\u0209\x03\x02\x02" + - "\x02\u0209\u020A\b?\x07\x02\u020A\u020B\b?\x05\x02\u020B\x80\x03\x02\x02" + - "\x02\u020C\u020D\x07_\x02\x02\u020D\u020E\x03\x02\x02\x02\u020E\u020F" + - "\b@\x05\x02\u020F\u0210\b@\x05\x02\u0210\u0211\b@\b\x02\u0211\x82\x03" + - "\x02\x02\x02\u0212\u0213\x07.\x02\x02\u0213\u0214\x03\x02\x02\x02\u0214" + - "\u0215\bA\t\x02\u0215\x84\x03\x02\x02\x02\u0216\u0217\x07?\x02\x02\u0217" + - "\u0218\x03\x02\x02\x02\u0218\u0219\bB\n\x02\u0219\x86\x03\x02\x02\x02" + - "\u021A\u021C\x05\x89D\x02\u021B\u021A\x03\x02\x02\x02\u021C\u021D\x03" + - "\x02\x02\x02\u021D\u021B\x03\x02\x02\x02\u021D\u021E\x03\x02\x02\x02\u021E" + - "\x88\x03\x02\x02\x02\u021F\u0221\n\v\x02\x02\u0220\u021F\x03\x02\x02\x02" + - "\u0221\u0222\x03\x02\x02\x02\u0222\u0220\x03\x02\x02\x02\u0222\u0223\x03" + - "\x02\x02\x02\u0223\u0227\x03\x02\x02\x02\u0224\u0225\x071\x02\x02\u0225" + - "\u0227\n\f\x02\x02\u0226\u0220\x03\x02\x02\x02\u0226\u0224\x03\x02\x02" + - "\x02\u0227\x8A\x03\x02\x02\x02\u0228\u0229\x05w;\x02\u0229\x8C\x03\x02" + - "\x02\x02\u022A\u022B\x05\x17\v"; - private static readonly _serializedATNSegment1: string = - "\x02\u022B\u022C\x03\x02\x02\x02\u022C\u022D\bF\x04\x02\u022D\x8E\x03" + - "\x02\x02\x02\u022E\u022F\x05\x19\f\x02\u022F\u0230\x03\x02\x02\x02\u0230" + - "\u0231\bG\x04\x02\u0231\x90\x03\x02\x02\x02\u0232\u0233\x05\x1B\r\x02" + - "\u0233\u0234\x03\x02\x02\x02\u0234\u0235\bH\x04\x02\u0235\x92\x03\x02" + - "\x02\x02\u0236\u0238\n\r\x02\x02\u0237\u0236\x03\x02\x02\x02\u0238\u0239" + - "\x03\x02\x02\x02\u0239\u0237\x03\x02\x02\x02\u0239\u023A\x03\x02\x02\x02" + - "\u023A\u023B\x03\x02\x02\x02\u023B\u023C\bI\x02\x02\u023C\x94\x03\x02" + - "\x02\x02%\x02\x03\x04\xE2\xE6\xE9\xF2\xF4\xFF\u0112\u0117\u011C\u011E" + - "\u0129\u0131\u0134\u0136\u013B\u0140\u0146\u014D\u0152\u0158\u015B\u0163" + - "\u0167\u01E6\u01EB\u01ED\u01F4\u01F6\u021D\u0222\u0226\u0239\v\x07\x03" + - "\x02\x07\x04\x02\x02\x03\x02\x06\x02\x02\x07\x02\x02\t\x0F\x02\t\x1F\x02" + - "\t\x17\x02\t\x16\x02"; - public static readonly _serializedATN: string = Utils.join( - [ - esql_lexer._serializedATNSegment0, - esql_lexer._serializedATNSegment1, - ], - "", - ); + ";\t;\x04<\t<\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03" + + "\x03\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x05\x03" + + "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03" + + "\x06\x03\x06\x03\x06\x03\x06\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03" + + "\x07\x03\x07\x03\x07\x03\b\x03\b\x03\b\x03\b\x03\b\x03\b\x03\b\x03\t\x03" + + "\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03\n\x03\n\x03\n\x03\n\x03\n\x03" + + "\n\x03\n\x03\n\x03\n\x03\n\x03\v\x03\v\x03\v\x03\v\x07\v\xC7\n\v\f\v\x0E" + + "\v\xCA\v\v\x03\v\x05\v\xCD\n\v\x03\v\x05\v\xD0\n\v\x03\v\x03\v\x03\f\x03" + + "\f\x03\f\x03\f\x03\f\x07\f\xD9\n\f\f\f\x0E\f\xDC\v\f\x03\f\x03\f\x03\f" + + "\x03\f\x03\f\x03\r\x06\r\xE4\n\r\r\r\x0E\r\xE5\x03\r\x03\r\x03\x0E\x03" + + "\x0E\x03\x0E\x03\x0E\x03\x0F\x03\x0F\x03\x10\x03\x10\x03\x11\x03\x11\x03" + + "\x11\x03\x12\x03\x12\x03\x13\x03\x13\x05\x13\xF9\n\x13\x03\x13\x06\x13" + + "\xFC\n\x13\r\x13\x0E\x13\xFD\x03\x14\x03\x14\x03\x14\x07\x14\u0103\n\x14" + + "\f\x14\x0E\x14\u0106\v\x14\x03\x14\x03\x14\x03\x14\x03\x14\x03\x14\x03" + + "\x14\x07\x14\u010E\n\x14\f\x14\x0E\x14\u0111\v\x14\x03\x14\x03\x14\x03" + + "\x14\x03\x14\x03\x14\x05\x14\u0118\n\x14\x03\x14\x05\x14\u011B\n\x14\x05" + + "\x14\u011D\n\x14\x03\x15\x06\x15\u0120\n\x15\r\x15\x0E\x15\u0121\x03\x16" + + "\x06\x16\u0125\n\x16\r\x16\x0E\x16\u0126\x03\x16\x03\x16\x07\x16\u012B" + + "\n\x16\f\x16\x0E\x16\u012E\v\x16\x03\x16\x03\x16\x06\x16\u0132\n\x16\r" + + "\x16\x0E\x16\u0133\x03\x16\x06\x16\u0137\n\x16\r\x16\x0E\x16\u0138\x03" + + "\x16\x03\x16\x07\x16\u013D\n\x16\f\x16\x0E\x16\u0140\v\x16\x05\x16\u0142" + + "\n\x16\x03\x16\x03\x16\x03\x16\x03\x16\x06\x16\u0148\n\x16\r\x16\x0E\x16" + + "\u0149\x03\x16\x03\x16\x05\x16\u014E\n\x16\x03\x17\x03\x17\x03\x17\x03" + + "\x18\x03\x18\x03\x18\x03\x18\x03\x19\x03\x19\x03\x1A\x03\x1A\x03\x1B\x03" + + "\x1B\x03\x1C\x03\x1C\x03\x1D\x03\x1D\x03\x1D\x03\x1D\x03\x1E\x03\x1E\x03" + + "\x1E\x03\x1E\x03\x1E\x03\x1F\x03\x1F\x03\x1F\x03\x1F\x03 \x03 \x03 \x03" + + " \x03 \x03!\x03!\x03!\x03\"\x03\"\x03#\x03#\x03#\x03#\x03#\x03#\x03#\x03" + + "#\x03#\x05#\u017F\n#\x03$\x03$\x03$\x03$\x03$\x03$\x03$\x03$\x03$\x03" + + "$\x05$\u018B\n$\x03%\x03%\x03&\x03&\x03\'\x03\'\x03(\x03(\x03)\x03)\x03" + + "*\x03*\x03*\x03*\x03*\x03*\x03*\x05*\u019E\n*\x03+\x03+\x03+\x03+\x03" + + "+\x03+\x03,\x03,\x03,\x03,\x03,\x03,\x03,\x03,\x03,\x05,\u01AF\n,\x03" + + "-\x03-\x03-\x03-\x03-\x03-\x03-\x03-\x03-\x03-\x03-\x03-\x03-\x03-\x03" + + "-\x03-\x03-\x05-\u01C2\n-\x03.\x03.\x05.\u01C6\n.\x03.\x03.\x03.\x07." + + "\u01CB\n.\f.\x0E.\u01CE\v.\x03/\x03/\x03/\x03/\x07/\u01D4\n/\f/\x0E/\u01D7" + + "\v/\x03/\x03/\x030\x030\x030\x030\x031\x031\x031\x031\x032\x032\x032\x03" + + "2\x033\x033\x033\x033\x033\x034\x034\x034\x034\x034\x034\x035\x035\x03" + + "5\x035\x036\x036\x036\x036\x037\x067\u01FB\n7\r7\x0E7\u01FC\x038\x068" + + "\u0200\n8\r8\x0E8\u0201\x038\x038\x058\u0206\n8\x039\x039\x03:\x03:\x03" + + ":\x03:\x03;\x03;\x03;\x03;\x03<\x03<\x03<\x03<\x04\xDA\u010F\x02\x02=" + + "\x05\x02\x03\x07\x02\x04\t\x02\x05\v\x02\x06\r\x02\x07\x0F\x02\b\x11\x02" + + "\t\x13\x02\n\x15\x02\v\x17\x02\f\x19\x02\r\x1B\x02\x0E\x1D\x02\x0F\x1F" + + "\x02\x02!\x02\x02#\x02\x02%\x02\x02\'\x02\x02)\x02\x10+\x02\x11-\x02\x12" + + "/\x02\x131\x02\x143\x02\x155\x02\x167\x02\x179\x02\x18;\x02\x19=\x02\x1A" + + "?\x02\x1BA\x02\x1CC\x02\x1DE\x02\x1EG\x02\x1FI\x02 K\x02!M\x02\"O\x02" + + "#Q\x02$S\x02%U\x02&W\x02\'Y\x02([\x02)]\x02*_\x02+a\x02,c\x02-e\x02.g" + + "\x02\x02i\x02\x02k\x02\x02m\x02\x02o\x02/q\x02\x02s\x020u\x021w\x022y" + + "\x023\x05\x02\x03\x04\r\x04\x02\f\f\x0F\x0F\x05\x02\v\f\x0F\x0F\"\"\x03" + + "\x022;\x04\x02C\\c|\x07\x02$$^^ppttvv\x06\x02\f\f\x0F\x0F$$^^\x04\x02" + + "GGgg\x04\x02--//\x03\x02bb\f\x02\v\f\x0F\x0F\"\"..11??]]__bb~~\x04\x02" + + ",,11\x02\u023A\x02\x05\x03\x02\x02\x02\x02\x07\x03\x02\x02\x02\x02\t\x03" + + "\x02\x02\x02\x02\v\x03\x02\x02\x02\x02\r\x03\x02\x02\x02\x02\x0F\x03\x02" + + "\x02\x02\x02\x11\x03\x02\x02\x02\x02\x13\x03\x02\x02\x02\x02\x15\x03\x02" + + "\x02\x02\x02\x17\x03\x02\x02\x02\x02\x19\x03\x02\x02\x02\x02\x1B\x03\x02" + + "\x02\x02\x03\x1D\x03\x02\x02\x02\x03)\x03\x02\x02\x02\x03+\x03\x02\x02" + + "\x02\x03-\x03\x02\x02\x02\x03/\x03\x02\x02\x02\x031\x03\x02\x02\x02\x03" + + "3\x03\x02\x02\x02\x035\x03\x02\x02\x02\x037\x03\x02\x02\x02\x039\x03\x02" + + "\x02\x02\x03;\x03\x02\x02\x02\x03=\x03\x02\x02\x02\x03?\x03\x02\x02\x02" + + "\x03A\x03\x02\x02\x02\x03C\x03\x02\x02\x02\x03E\x03\x02\x02\x02\x03G\x03" + + "\x02\x02\x02\x03I\x03\x02\x02\x02\x03K\x03\x02\x02\x02\x03M\x03\x02\x02" + + "\x02\x03O\x03\x02\x02\x02\x03Q\x03\x02\x02\x02\x03S\x03\x02\x02\x02\x03" + + "U\x03\x02\x02\x02\x03W\x03\x02\x02\x02\x03Y\x03\x02\x02\x02\x03[\x03\x02" + + "\x02\x02\x03]\x03\x02\x02\x02\x03_\x03\x02\x02\x02\x03a\x03\x02\x02\x02" + + "\x03c\x03\x02\x02\x02\x03e\x03\x02\x02\x02\x04g\x03\x02\x02\x02\x04i\x03" + + "\x02\x02\x02\x04k\x03\x02\x02\x02\x04m\x03\x02\x02\x02\x04o\x03\x02\x02" + + "\x02\x04s\x03\x02\x02\x02\x04u\x03\x02\x02\x02\x04w\x03\x02\x02\x02\x04" + + "y\x03\x02\x02\x02\x05{\x03\x02\x02\x02\x07\x82\x03\x02\x02\x02\t\x8C\x03" + + "\x02\x02\x02\v\x93\x03\x02\x02\x02\r\x99\x03\x02\x02\x02\x0F\xA1\x03\x02" + + "\x02\x02\x11\xA9\x03\x02\x02\x02\x13\xB0\x03\x02\x02\x02\x15\xB8\x03\x02" + + "\x02\x02\x17\xC2\x03\x02\x02\x02\x19\xD3\x03\x02\x02\x02\x1B\xE3\x03\x02" + + "\x02\x02\x1D\xE9\x03\x02\x02\x02\x1F\xED\x03\x02\x02\x02!\xEF\x03\x02" + + "\x02\x02#\xF1\x03\x02\x02\x02%\xF4\x03\x02\x02\x02\'\xF6\x03\x02\x02\x02" + + ")\u011C\x03\x02\x02\x02+\u011F\x03\x02\x02\x02-\u014D\x03\x02\x02\x02" + + "/\u014F\x03\x02\x02\x021\u0152\x03\x02\x02\x023\u0156\x03\x02\x02\x02" + + "5\u0158\x03\x02\x02\x027\u015A\x03\x02\x02\x029\u015C\x03\x02\x02\x02" + + ";\u015E\x03\x02\x02\x02=\u0162\x03\x02\x02\x02?\u0167\x03\x02\x02\x02" + + "A\u016B\x03\x02\x02\x02C\u0170\x03\x02\x02\x02E\u0173\x03\x02\x02\x02" + + "G\u017E\x03\x02\x02\x02I\u018A\x03\x02\x02\x02K\u018C\x03\x02\x02\x02" + + "M\u018E\x03\x02\x02\x02O\u0190\x03\x02\x02\x02Q\u0192\x03\x02\x02\x02" + + "S\u0194\x03\x02\x02\x02U\u019D\x03\x02\x02\x02W\u019F\x03\x02\x02\x02" + + "Y\u01AE\x03\x02\x02\x02[\u01C1\x03\x02\x02\x02]\u01C5\x03\x02\x02\x02" + + "_\u01CF\x03\x02\x02\x02a\u01DA\x03\x02\x02\x02c\u01DE\x03\x02\x02\x02" + + "e\u01E2\x03\x02\x02\x02g\u01E6\x03\x02\x02\x02i\u01EB\x03\x02\x02\x02" + + "k\u01F1\x03\x02\x02\x02m\u01F5\x03\x02\x02\x02o\u01FA\x03\x02\x02\x02" + + "q\u0205\x03\x02\x02\x02s\u0207\x03\x02\x02\x02u\u0209\x03\x02\x02\x02" + + "w\u020D\x03\x02\x02\x02y\u0211\x03\x02\x02\x02{|\x07g\x02\x02|}\x07x\x02" + + "\x02}~\x07c\x02\x02~\x7F\x07n\x02\x02\x7F\x80\x03\x02\x02\x02\x80\x81" + + "\b\x02\x02\x02\x81\x06\x03\x02\x02\x02\x82\x83\x07g\x02\x02\x83\x84\x07" + + "z\x02\x02\x84\x85\x07r\x02\x02\x85\x86\x07n\x02\x02\x86\x87\x07c\x02\x02" + + "\x87\x88\x07k\x02\x02\x88\x89\x07p\x02\x02\x89\x8A\x03\x02\x02\x02\x8A" + + "\x8B\b\x03\x02\x02\x8B\b\x03\x02\x02\x02\x8C\x8D\x07h\x02\x02\x8D\x8E" + + "\x07t\x02\x02\x8E\x8F\x07q\x02\x02\x8F\x90\x07o\x02\x02\x90\x91\x03\x02" + + "\x02\x02\x91\x92\b\x04\x03\x02\x92\n\x03\x02\x02\x02\x93\x94\x07t\x02" + + "\x02\x94\x95\x07q\x02\x02\x95\x96\x07y\x02\x02\x96\x97\x03\x02\x02\x02" + + "\x97\x98\b\x05\x02\x02\x98\f\x03\x02\x02\x02\x99\x9A\x07u\x02\x02\x9A" + + "\x9B\x07v\x02\x02\x9B\x9C\x07c\x02\x02\x9C\x9D\x07v\x02\x02\x9D\x9E\x07" + + "u\x02\x02\x9E\x9F\x03\x02\x02\x02\x9F\xA0\b\x06\x02\x02\xA0\x0E\x03\x02" + + "\x02\x02\xA1\xA2\x07y\x02\x02\xA2\xA3\x07j\x02\x02\xA3\xA4\x07g\x02\x02" + + "\xA4\xA5\x07t\x02\x02\xA5\xA6\x07g\x02\x02\xA6\xA7\x03\x02\x02\x02\xA7" + + "\xA8\b\x07\x02\x02\xA8\x10\x03\x02\x02\x02\xA9\xAA\x07u\x02\x02\xAA\xAB" + + "\x07q\x02\x02\xAB\xAC\x07t\x02\x02\xAC\xAD\x07v\x02\x02\xAD\xAE\x03\x02" + + "\x02\x02\xAE\xAF\b\b\x02\x02\xAF\x12\x03\x02\x02\x02\xB0\xB1\x07n\x02" + + "\x02\xB1\xB2\x07k\x02\x02\xB2\xB3\x07o\x02\x02\xB3\xB4\x07k\x02\x02\xB4" + + "\xB5\x07v\x02\x02\xB5\xB6\x03\x02\x02\x02\xB6\xB7\b\t\x02\x02\xB7\x14" + + "\x03\x02\x02\x02\xB8\xB9\x07r\x02\x02\xB9\xBA\x07t\x02\x02\xBA\xBB\x07" + + "q\x02\x02\xBB\xBC\x07l\x02\x02\xBC\xBD\x07g\x02\x02\xBD\xBE\x07e\x02\x02" + + "\xBE\xBF\x07v\x02\x02\xBF\xC0\x03\x02\x02\x02\xC0\xC1\b\n\x03\x02\xC1" + + "\x16\x03\x02\x02\x02\xC2\xC3\x071\x02\x02\xC3\xC4\x071\x02\x02\xC4\xC8" + + "\x03\x02\x02\x02\xC5\xC7\n\x02\x02\x02\xC6\xC5\x03\x02\x02\x02\xC7\xCA" + + "\x03\x02\x02\x02\xC8\xC6\x03\x02\x02\x02\xC8\xC9\x03\x02\x02\x02\xC9\xCC" + + "\x03\x02\x02\x02\xCA\xC8\x03\x02\x02\x02\xCB\xCD\x07\x0F\x02\x02\xCC\xCB" + + "\x03\x02\x02\x02\xCC\xCD\x03\x02\x02\x02\xCD\xCF\x03\x02\x02\x02\xCE\xD0" + + "\x07\f\x02\x02\xCF\xCE\x03\x02\x02\x02\xCF\xD0\x03\x02\x02\x02\xD0\xD1" + + "\x03\x02\x02\x02\xD1\xD2\b\v\x04\x02\xD2\x18\x03\x02\x02\x02\xD3\xD4\x07" + + "1\x02\x02\xD4\xD5\x07,\x02\x02\xD5\xDA\x03\x02\x02\x02\xD6\xD9\x05\x19" + + "\f\x02\xD7\xD9\v\x02\x02\x02\xD8\xD6\x03\x02\x02\x02\xD8\xD7\x03\x02\x02" + + "\x02\xD9\xDC\x03\x02\x02\x02\xDA\xDB\x03\x02\x02\x02\xDA\xD8\x03\x02\x02" + + "\x02\xDB\xDD\x03\x02\x02\x02\xDC\xDA\x03\x02\x02\x02\xDD\xDE\x07,\x02" + + "\x02\xDE\xDF\x071\x02\x02\xDF\xE0\x03\x02\x02\x02\xE0\xE1\b\f\x04\x02" + + "\xE1\x1A\x03\x02\x02\x02\xE2\xE4\t\x03\x02\x02\xE3\xE2\x03\x02\x02\x02" + + "\xE4\xE5\x03\x02\x02\x02\xE5\xE3\x03\x02\x02\x02\xE5\xE6\x03\x02\x02\x02" + + "\xE6\xE7\x03\x02\x02\x02\xE7\xE8\b\r\x04\x02\xE8\x1C\x03\x02\x02\x02\xE9" + + "\xEA\x07~\x02\x02\xEA\xEB\x03\x02\x02\x02\xEB\xEC\b\x0E\x05\x02\xEC\x1E" + + "\x03\x02\x02\x02\xED\xEE\t\x04\x02\x02\xEE \x03\x02\x02\x02\xEF\xF0\t" + + "\x05\x02\x02\xF0\"\x03\x02\x02\x02\xF1\xF2\x07^\x02\x02\xF2\xF3\t\x06" + + "\x02\x02\xF3$\x03\x02\x02\x02\xF4\xF5\n\x07\x02\x02\xF5&\x03\x02\x02\x02" + + "\xF6\xF8\t\b\x02\x02\xF7\xF9\t\t\x02\x02\xF8\xF7\x03\x02\x02\x02\xF8\xF9" + + "\x03\x02\x02\x02\xF9\xFB\x03\x02\x02\x02\xFA\xFC\x05\x1F\x0F\x02\xFB\xFA" + + "\x03\x02\x02\x02\xFC\xFD\x03\x02\x02\x02\xFD\xFB\x03\x02\x02\x02\xFD\xFE" + + "\x03\x02\x02\x02\xFE(\x03\x02\x02\x02\xFF\u0104\x07$\x02\x02\u0100\u0103" + + "\x05#\x11\x02\u0101\u0103\x05%\x12\x02\u0102\u0100\x03\x02\x02\x02\u0102" + + "\u0101\x03\x02\x02\x02\u0103\u0106\x03\x02\x02\x02\u0104\u0102\x03\x02" + + "\x02\x02\u0104\u0105\x03\x02\x02\x02\u0105\u0107\x03\x02\x02\x02\u0106" + + "\u0104\x03\x02\x02\x02\u0107\u011D\x07$\x02\x02\u0108\u0109\x07$\x02\x02" + + "\u0109\u010A\x07$\x02\x02\u010A\u010B\x07$\x02\x02\u010B\u010F\x03\x02" + + "\x02\x02\u010C\u010E\n\x02\x02\x02\u010D\u010C\x03\x02\x02\x02\u010E\u0111" + + "\x03\x02\x02\x02\u010F\u0110\x03\x02\x02\x02\u010F\u010D\x03\x02\x02\x02" + + "\u0110\u0112\x03\x02\x02\x02\u0111\u010F\x03\x02\x02\x02\u0112\u0113\x07" + + "$\x02\x02\u0113\u0114\x07$\x02\x02\u0114\u0115\x07$\x02\x02\u0115\u0117" + + "\x03\x02\x02\x02\u0116\u0118\x07$\x02\x02\u0117\u0116\x03\x02\x02\x02" + + "\u0117\u0118\x03\x02\x02\x02\u0118\u011A\x03\x02\x02\x02\u0119\u011B\x07" + + "$\x02\x02\u011A\u0119\x03\x02\x02\x02\u011A\u011B\x03\x02\x02\x02\u011B" + + "\u011D\x03\x02\x02\x02\u011C\xFF\x03\x02\x02\x02\u011C\u0108\x03\x02\x02" + + "\x02\u011D*\x03\x02\x02\x02\u011E\u0120\x05\x1F\x0F\x02\u011F\u011E\x03" + + "\x02\x02\x02\u0120\u0121\x03\x02\x02\x02\u0121\u011F\x03\x02\x02\x02\u0121" + + "\u0122\x03\x02\x02\x02\u0122,\x03\x02\x02\x02\u0123\u0125\x05\x1F\x0F" + + "\x02\u0124\u0123\x03\x02\x02\x02\u0125\u0126\x03\x02\x02\x02\u0126\u0124" + + "\x03\x02\x02\x02\u0126\u0127\x03\x02\x02\x02\u0127\u0128\x03\x02\x02\x02" + + "\u0128\u012C\x057\x1B\x02\u0129\u012B\x05\x1F\x0F\x02\u012A\u0129\x03" + + "\x02\x02\x02\u012B\u012E\x03\x02\x02\x02\u012C\u012A\x03\x02\x02\x02\u012C" + + "\u012D\x03\x02\x02\x02\u012D\u014E\x03\x02\x02\x02\u012E\u012C\x03\x02" + + "\x02\x02\u012F\u0131\x057\x1B\x02\u0130\u0132\x05\x1F\x0F\x02\u0131\u0130" + + "\x03\x02\x02\x02\u0132\u0133\x03\x02\x02\x02\u0133\u0131\x03\x02\x02\x02" + + "\u0133\u0134\x03\x02\x02\x02\u0134\u014E\x03\x02\x02\x02\u0135\u0137\x05" + + "\x1F\x0F\x02\u0136\u0135\x03\x02\x02\x02\u0137\u0138\x03\x02\x02\x02\u0138" + + "\u0136\x03\x02\x02\x02\u0138\u0139\x03\x02\x02\x02\u0139\u0141\x03\x02" + + "\x02\x02\u013A\u013E\x057\x1B\x02\u013B\u013D\x05\x1F\x0F\x02\u013C\u013B" + + "\x03\x02\x02\x02\u013D\u0140\x03\x02\x02\x02\u013E\u013C\x03\x02\x02\x02" + + "\u013E\u013F\x03\x02\x02\x02\u013F\u0142\x03\x02\x02\x02\u0140\u013E\x03" + + "\x02\x02\x02\u0141\u013A\x03\x02\x02\x02\u0141\u0142\x03\x02\x02\x02\u0142" + + "\u0143\x03\x02\x02\x02\u0143\u0144\x05\'\x13\x02\u0144\u014E\x03\x02\x02" + + "\x02\u0145\u0147\x057\x1B\x02\u0146\u0148\x05\x1F\x0F\x02\u0147\u0146" + + "\x03\x02\x02\x02\u0148\u0149\x03\x02\x02\x02\u0149\u0147\x03\x02\x02\x02" + + "\u0149\u014A\x03\x02\x02\x02\u014A\u014B\x03\x02\x02\x02\u014B\u014C\x05" + + "\'\x13\x02\u014C\u014E\x03\x02\x02\x02\u014D\u0124\x03\x02\x02\x02\u014D" + + "\u012F\x03\x02\x02\x02\u014D\u0136\x03\x02\x02\x02\u014D\u0145\x03\x02" + + "\x02\x02\u014E.\x03\x02\x02\x02\u014F\u0150\x07d\x02\x02\u0150\u0151\x07" + + "{\x02\x02\u01510\x03\x02\x02\x02\u0152\u0153\x07c\x02\x02\u0153\u0154" + + "\x07p\x02\x02\u0154\u0155\x07f\x02\x02\u01552\x03\x02\x02\x02\u0156\u0157" + + "\x07?\x02\x02\u01574\x03\x02\x02\x02\u0158\u0159\x07.\x02\x02\u01596\x03" + + "\x02\x02\x02\u015A\u015B\x070\x02\x02\u015B8\x03\x02\x02\x02\u015C\u015D" + + "\x07*\x02\x02\u015D:\x03\x02\x02\x02\u015E\u015F\x07]\x02\x02\u015F\u0160" + + "\x03\x02\x02\x02\u0160\u0161\b\x1D\x06\x02\u0161<\x03\x02\x02\x02\u0162" + + "\u0163\x07_\x02\x02\u0163\u0164\x03\x02\x02\x02\u0164\u0165\b\x1E\x05" + + "\x02\u0165\u0166\b\x1E\x05\x02\u0166>\x03\x02\x02\x02\u0167\u0168\x07" + + "p\x02\x02\u0168\u0169\x07q\x02\x02\u0169\u016A\x07v\x02\x02\u016A@\x03" + + "\x02\x02\x02\u016B\u016C\x07p\x02\x02\u016C\u016D\x07w\x02\x02\u016D\u016E" + + "\x07n\x02\x02\u016E\u016F\x07n\x02\x02\u016FB\x03\x02\x02\x02\u0170\u0171" + + "\x07q\x02\x02\u0171\u0172\x07t\x02\x02\u0172D\x03\x02\x02\x02\u0173\u0174" + + "\x07+\x02\x02\u0174F\x03\x02\x02\x02\u0175\u0176\x07v\x02\x02\u0176\u0177" + + "\x07t\x02\x02\u0177\u0178\x07w\x02\x02\u0178\u017F\x07g\x02\x02\u0179" + + "\u017A\x07h\x02\x02\u017A\u017B\x07c\x02\x02\u017B\u017C\x07n\x02\x02" + + "\u017C\u017D\x07u\x02\x02\u017D\u017F\x07g\x02\x02\u017E\u0175\x03\x02" + + "\x02\x02\u017E\u0179\x03\x02\x02\x02\u017FH\x03\x02\x02\x02\u0180\u0181" + + "\x07?\x02\x02\u0181\u018B\x07?\x02\x02\u0182\u0183\x07#\x02\x02\u0183" + + "\u018B\x07?\x02\x02\u0184\u018B\x07>\x02\x02\u0185\u0186\x07>\x02\x02" + + "\u0186\u018B\x07?\x02\x02\u0187\u018B\x07@\x02\x02\u0188\u0189\x07@\x02" + + "\x02\u0189\u018B\x07?\x02\x02\u018A\u0180\x03\x02\x02\x02\u018A\u0182" + + "\x03\x02\x02\x02\u018A\u0184\x03\x02\x02\x02\u018A\u0185\x03\x02\x02\x02" + + "\u018A\u0187\x03\x02\x02\x02\u018A\u0188\x03\x02\x02\x02\u018BJ\x03\x02" + + "\x02\x02\u018C\u018D\x07-\x02\x02\u018DL\x03\x02\x02\x02\u018E\u018F\x07" + + "/\x02\x02\u018FN\x03\x02\x02\x02\u0190\u0191\x07,\x02\x02\u0191P\x03\x02" + + "\x02\x02\u0192\u0193\x071\x02\x02\u0193R\x03\x02\x02\x02\u0194\u0195\x07" + + "\'\x02\x02\u0195T\x03\x02\x02\x02\u0196\u0197\x07c\x02\x02\u0197\u0198" + + "\x07u\x02\x02\u0198\u019E\x07e\x02\x02\u0199\u019A\x07f\x02\x02\u019A" + + "\u019B\x07g\x02\x02\u019B\u019C\x07u\x02\x02\u019C\u019E\x07e\x02\x02" + + "\u019D\u0196\x03\x02\x02\x02\u019D\u0199\x03\x02\x02\x02\u019EV\x03\x02" + + "\x02\x02\u019F\u01A0\x07p\x02\x02\u01A0\u01A1\x07w\x02\x02\u01A1\u01A2" + + "\x07n\x02\x02\u01A2\u01A3\x07n\x02\x02\u01A3\u01A4\x07u\x02\x02\u01A4" + + "X\x03\x02\x02\x02\u01A5\u01A6\x07h\x02\x02\u01A6\u01A7\x07k\x02\x02\u01A7" + + "\u01A8\x07t\x02\x02\u01A8\u01A9\x07u\x02\x02\u01A9\u01AF\x07v\x02\x02" + + "\u01AA\u01AB\x07n\x02\x02\u01AB\u01AC\x07c\x02\x02\u01AC\u01AD\x07u\x02" + + "\x02\u01AD\u01AF\x07v\x02\x02\u01AE\u01A5\x03\x02\x02\x02\u01AE\u01AA" + + "\x03\x02\x02\x02\u01AFZ\x03\x02\x02\x02\u01B0\u01B1\x07t\x02\x02\u01B1" + + "\u01B2\x07q\x02\x02\u01B2\u01B3\x07w\x02\x02\u01B3\u01B4\x07p\x02\x02" + + "\u01B4\u01C2\x07f\x02\x02\u01B5\u01B6\x07c\x02\x02\u01B6\u01B7\x07x\x02" + + "\x02\u01B7\u01C2\x07i\x02\x02\u01B8\u01B9\x07o\x02\x02\u01B9\u01BA\x07" + + "k\x02\x02\u01BA\u01C2\x07p\x02\x02\u01BB\u01BC\x07o\x02\x02\u01BC\u01BD" + + "\x07c\x02\x02\u01BD\u01C2\x07z\x02\x02\u01BE\u01BF\x07u\x02\x02\u01BF" + + "\u01C0\x07w\x02\x02\u01C0\u01C2\x07o\x02\x02\u01C1\u01B0\x03\x02\x02\x02" + + "\u01C1\u01B5\x03\x02\x02\x02\u01C1\u01B8\x03\x02\x02\x02\u01C1\u01BB\x03" + + "\x02\x02\x02\u01C1\u01BE\x03\x02\x02\x02\u01C2\\\x03\x02\x02\x02\u01C3" + + "\u01C6\x05!\x10\x02\u01C4\u01C6\x07a\x02\x02\u01C5\u01C3\x03\x02\x02\x02" + + "\u01C5\u01C4\x03\x02\x02\x02\u01C6\u01CC\x03\x02\x02\x02\u01C7\u01CB\x05" + + "!\x10\x02\u01C8\u01CB\x05\x1F\x0F\x02\u01C9\u01CB\x07a\x02\x02\u01CA\u01C7" + + "\x03\x02\x02\x02\u01CA\u01C8\x03\x02\x02\x02\u01CA\u01C9\x03\x02\x02\x02" + + "\u01CB\u01CE\x03\x02\x02\x02\u01CC\u01CA\x03\x02\x02\x02\u01CC\u01CD\x03" + + "\x02\x02\x02\u01CD^\x03\x02\x02\x02\u01CE\u01CC\x03\x02\x02\x02\u01CF" + + "\u01D5\x07b\x02\x02\u01D0\u01D4\n\n\x02\x02\u01D1\u01D2\x07b\x02\x02\u01D2" + + "\u01D4\x07b\x02\x02\u01D3\u01D0\x03\x02\x02\x02\u01D3\u01D1\x03\x02\x02" + + "\x02\u01D4\u01D7\x03\x02\x02\x02\u01D5\u01D3\x03\x02\x02\x02\u01D5\u01D6" + + "\x03\x02\x02\x02\u01D6\u01D8\x03\x02\x02\x02\u01D7\u01D5\x03\x02\x02\x02" + + "\u01D8\u01D9\x07b\x02\x02\u01D9`\x03\x02\x02\x02\u01DA\u01DB\x05\x17\v" + + "\x02\u01DB\u01DC\x03\x02\x02\x02\u01DC\u01DD\b0\x04\x02\u01DDb\x03\x02" + + "\x02\x02\u01DE\u01DF\x05\x19\f\x02\u01DF\u01E0\x03\x02\x02\x02\u01E0\u01E1" + + "\b1\x04\x02\u01E1d\x03\x02\x02\x02\u01E2\u01E3\x05\x1B\r\x02\u01E3\u01E4" + + "\x03\x02\x02\x02\u01E4\u01E5\b2\x04\x02\u01E5f\x03\x02\x02\x02\u01E6\u01E7" + + "\x07~\x02\x02\u01E7\u01E8\x03\x02\x02\x02\u01E8\u01E9\b3\x07\x02\u01E9" + + "\u01EA\b3\x05\x02\u01EAh\x03\x02\x02\x02\u01EB\u01EC\x07_\x02\x02\u01EC" + + "\u01ED\x03\x02\x02\x02\u01ED\u01EE\b4\x05\x02\u01EE\u01EF\b4\x05\x02\u01EF" + + "\u01F0\b4\b\x02\u01F0j\x03\x02\x02\x02\u01F1\u01F2\x07.\x02\x02\u01F2" + + "\u01F3\x03\x02\x02\x02\u01F3\u01F4\b5\t\x02\u01F4l\x03\x02\x02\x02\u01F5" + + "\u01F6\x07?\x02\x02\u01F6\u01F7\x03\x02\x02\x02\u01F7\u01F8\b6\n\x02\u01F8" + + "n\x03\x02\x02\x02\u01F9\u01FB\x05q8\x02\u01FA\u01F9\x03\x02\x02\x02\u01FB" + + "\u01FC\x03\x02\x02\x02\u01FC\u01FA\x03\x02\x02\x02\u01FC\u01FD\x03\x02" + + "\x02\x02\u01FDp\x03\x02\x02\x02\u01FE\u0200\n\v\x02\x02\u01FF\u01FE\x03" + + "\x02\x02\x02\u0200\u0201\x03\x02\x02\x02\u0201\u01FF\x03\x02\x02\x02\u0201" + + "\u0202\x03\x02\x02\x02\u0202\u0206\x03\x02\x02\x02\u0203\u0204\x071\x02" + + "\x02\u0204\u0206\n\f\x02\x02\u0205\u01FF\x03\x02\x02\x02\u0205\u0203\x03" + + "\x02\x02\x02\u0206r\x03\x02\x02\x02\u0207\u0208\x05_/\x02\u0208t\x03\x02" + + "\x02\x02\u0209\u020A\x05\x17\v\x02\u020A\u020B\x03\x02\x02\x02\u020B\u020C" + + "\b:\x04\x02\u020Cv\x03\x02\x02\x02\u020D\u020E\x05\x19\f\x02\u020E\u020F" + + "\x03\x02\x02\x02\u020F\u0210\b;\x04\x02\u0210x\x03\x02\x02\x02\u0211\u0212" + + "\x05\x1B\r\x02\u0212\u0213\x03\x02\x02\x02\u0213\u0214\b<\x04\x02\u0214" + + "z\x03\x02\x02\x02)\x02\x03\x04\xC8\xCC\xCF\xD8\xDA\xE5\xF8\xFD\u0102\u0104" + + "\u010F\u0117\u011A\u011C\u0121\u0126\u012C\u0133\u0138\u013E\u0141\u0149" + + "\u014D\u017E\u018A\u019D\u01AE\u01C1\u01C5\u01CA\u01CC\u01D3\u01D5\u01FC" + + "\u0201\u0205\v\x07\x03\x02\x07\x04\x02\x02\x03\x02\x06\x02\x02\x07\x02" + + "\x02\t\x0F\x02\t\x1A\x02\t\x16\x02\t\x15\x02"; public static __ATN: ATN; public static get _ATN(): ATN { if (!esql_lexer.__ATN) { diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 b/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 index f3141d22c5c8..6196874af91b 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 @@ -39,30 +39,38 @@ whereCommand ; booleanExpression - : NOT booleanExpression #logicalNot - | valueExpression #booleanDefault - | left=booleanExpression operator=AND right=booleanExpression #logicalBinary - | left=booleanExpression operator=OR right=booleanExpression #logicalBinary + : NOT booleanExpression + | valueExpression + | left=booleanExpression operator=AND right=booleanExpression + | left=booleanExpression operator=OR right=booleanExpression ; valueExpression - : functionIdentifier LP (functionExpressionArgument (COMMA functionExpressionArgument)*)? RP #valueFunctionExpression - | operatorExpression #valueExpressionDefault - | left=operatorExpression comparisonOperator right=operatorExpression #comparison + : operatorExpression + | comparison + ; + +comparison + : left=operatorExpression comparisonOperator right=operatorExpression + ; + +mathFn + : functionIdentifier LP (functionExpressionArgument (COMMA functionExpressionArgument)*)? RP ; operatorExpression - : primaryExpression #operatorExpressionDefault - | operator=(MINUS | PLUS) operatorExpression #arithmeticUnary - | left=operatorExpression operator=(ASTERISK | SLASH | PERCENT) right=operatorExpression #arithmeticBinary - | left=operatorExpression operator=(PLUS | MINUS) right=operatorExpression #arithmeticBinary + : primaryExpression + | mathFn + | operator=(MINUS | PLUS) operatorExpression + | left=operatorExpression operator=(ASTERISK | SLASH | PERCENT) right=operatorExpression + | left=operatorExpression operator=(PLUS | MINUS) right=operatorExpression ; primaryExpression - : constant #constantDefault - | qualifiedName #dereference - | LP booleanExpression RP #parenthesizedExpression - | identifier LP (booleanExpression (COMMA booleanExpression)*)? RP #functionExpression + : constant + | qualifiedName + | LP booleanExpression RP + | identifier LP (booleanExpression (COMMA booleanExpression)*)? RP ; rowCommand @@ -74,11 +82,14 @@ fields ; field - : qualifiedName ASSIGN valueExpression - | booleanExpression - | qualifiedName ASSIGN booleanExpression + : booleanExpression + | userVariable ASSIGN booleanExpression ; +userVariable + : identifier + ; + fromCommand : FROM sourceIdentifier (COMMA sourceIdentifier)* ; @@ -115,14 +126,9 @@ identifier ; functionIdentifier - : ROUND_FUNCTION_MATH - | AVG_FUNCTION_MATH - | SUM_FUNCTION_MATH - | MIN_FUNCTION_MATH - | MAX_FUNCTION_MATH + : UNARY_FUNCTION ; - constant : NULL #nullLiteral | number #numericLiteral @@ -139,7 +145,7 @@ sortCommand ; orderExpression - : booleanExpression ordering=(ASC | DESC)? (NULLS nullOrdering=(FIRST | LAST))? + : booleanExpression (ORDERING)? (NULLS_ORDERING (NULLS_ORDERING_DIRECTION))? ; projectCommand @@ -152,7 +158,7 @@ projectClause ; booleanValue - : TRUE | FALSE + : BOOLEAN_VALUE ; number @@ -165,7 +171,7 @@ string ; comparisonOperator - : EQ | NEQ | LT | LTE | GT | GTE + : COMPARISON_OPERATOR ; explainCommand diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.interp b/packages/kbn-monaco/src/esql/antlr/esql_parser.interp index 8355a7acb9fb..39dc1a09fb8b 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.interp +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.interp @@ -18,39 +18,26 @@ null null 'by' 'and' -'asc' null null -'desc' '.' -'false' -'first' -'last' '(' '[' ']' 'not' 'null' -'nulls' 'or' ')' -'true' -'==' -'!=' -'<' -'<=' -'>' -'>=' +null +null '+' '-' '*' '/' '%' -'round' -'avg' -'sum' -'min' -'max' +null +'nulls' +null null null null @@ -83,39 +70,27 @@ INTEGER_LITERAL DECIMAL_LITERAL BY AND -ASC ASSIGN COMMA -DESC DOT -FALSE -FIRST -LAST LP OPENING_BRACKET CLOSING_BRACKET NOT NULL -NULLS OR RP -TRUE -EQ -NEQ -LT -LTE -GT -GTE +BOOLEAN_VALUE +COMPARISON_OPERATOR PLUS MINUS ASTERISK SLASH PERCENT -ROUND_FUNCTION_MATH -AVG_FUNCTION_MATH -SUM_FUNCTION_MATH -MIN_FUNCTION_MATH -MAX_FUNCTION_MATH +ORDERING +NULLS_ORDERING +NULLS_ORDERING_DIRECTION +UNARY_FUNCTION UNQUOTED_IDENTIFIER QUOTED_IDENTIFIER EXPR_LINE_COMMENT @@ -126,7 +101,6 @@ SRC_QUOTED_IDENTIFIER SRC_LINE_COMMENT SRC_MULTILINE_COMMENT SRC_WS -UNKNOWN_CMD rule names: singleStatement @@ -136,11 +110,14 @@ processingCommand whereCommand booleanExpression valueExpression +comparison +mathFn operatorExpression primaryExpression rowCommand fields field +userVariable fromCommand evalCommand statsCommand @@ -165,4 +142,4 @@ subqueryExpression atn: -[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 3, 64, 301, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 78, 10, 3, 12, 3, 14, 3, 81, 11, 3, 3, 4, 3, 4, 3, 4, 5, 4, 86, 10, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 5, 5, 94, 10, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 103, 10, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 7, 7, 111, 10, 7, 12, 7, 14, 7, 114, 11, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 7, 8, 121, 10, 8, 12, 8, 14, 8, 124, 11, 8, 5, 8, 126, 10, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 135, 10, 8, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 141, 10, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 7, 9, 149, 10, 9, 12, 9, 14, 9, 152, 11, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 165, 10, 10, 12, 10, 14, 10, 168, 11, 10, 5, 10, 170, 10, 10, 3, 10, 3, 10, 5, 10, 174, 10, 10, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 7, 12, 182, 10, 12, 12, 12, 14, 12, 185, 11, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 5, 13, 196, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 7, 14, 202, 10, 14, 12, 14, 14, 14, 205, 11, 14, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 5, 16, 214, 10, 16, 3, 17, 3, 17, 3, 18, 3, 18, 5, 18, 220, 10, 18, 3, 19, 3, 19, 3, 19, 7, 19, 225, 10, 19, 12, 19, 14, 19, 228, 11, 19, 3, 20, 3, 20, 3, 20, 7, 20, 233, 10, 20, 12, 20, 14, 20, 236, 11, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 3, 23, 5, 23, 246, 10, 23, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 25, 7, 25, 255, 10, 25, 12, 25, 14, 25, 258, 11, 25, 3, 26, 3, 26, 5, 26, 262, 10, 26, 3, 26, 3, 26, 5, 26, 266, 10, 26, 3, 27, 3, 27, 3, 27, 3, 27, 7, 27, 272, 10, 27, 12, 27, 14, 27, 275, 11, 27, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 5, 28, 282, 10, 28, 3, 29, 3, 29, 3, 30, 3, 30, 5, 30, 288, 10, 30, 3, 31, 3, 31, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 34, 3, 34, 2, 2, 5, 4, 12, 16, 35, 2, 2, 4, 2, 6, 2, 8, 2, 10, 2, 12, 2, 14, 2, 16, 2, 18, 2, 20, 2, 22, 2, 24, 2, 26, 2, 28, 2, 30, 2, 32, 2, 34, 2, 36, 2, 38, 2, 40, 2, 42, 2, 44, 2, 46, 2, 48, 2, 50, 2, 52, 2, 54, 2, 56, 2, 58, 2, 60, 2, 62, 2, 64, 2, 66, 2, 2, 11, 3, 2, 44, 45, 3, 2, 46, 48, 3, 2, 59, 60, 3, 2, 54, 55, 3, 2, 49, 53, 4, 2, 21, 21, 24, 24, 3, 2, 27, 28, 4, 2, 26, 26, 37, 37, 3, 2, 38, 43, 2, 307, 2, 68, 3, 2, 2, 2, 4, 71, 3, 2, 2, 2, 6, 85, 3, 2, 2, 2, 8, 93, 3, 2, 2, 2, 10, 95, 3, 2, 2, 2, 12, 102, 3, 2, 2, 2, 14, 134, 3, 2, 2, 2, 16, 140, 3, 2, 2, 2, 18, 173, 3, 2, 2, 2, 20, 175, 3, 2, 2, 2, 22, 178, 3, 2, 2, 2, 24, 195, 3, 2, 2, 2, 26, 197, 3, 2, 2, 2, 28, 206, 3, 2, 2, 2, 30, 209, 3, 2, 2, 2, 32, 215, 3, 2, 2, 2, 34, 219, 3, 2, 2, 2, 36, 221, 3, 2, 2, 2, 38, 229, 3, 2, 2, 2, 40, 237, 3, 2, 2, 2, 42, 239, 3, 2, 2, 2, 44, 245, 3, 2, 2, 2, 46, 247, 3, 2, 2, 2, 48, 250, 3, 2, 2, 2, 50, 259, 3, 2, 2, 2, 52, 267, 3, 2, 2, 2, 54, 281, 3, 2, 2, 2, 56, 283, 3, 2, 2, 2, 58, 287, 3, 2, 2, 2, 60, 289, 3, 2, 2, 2, 62, 291, 3, 2, 2, 2, 64, 293, 3, 2, 2, 2, 66, 296, 3, 2, 2, 2, 68, 69, 5, 4, 3, 2, 69, 70, 7, 2, 2, 3, 70, 3, 3, 2, 2, 2, 71, 72, 8, 3, 1, 2, 72, 73, 5, 6, 4, 2, 73, 79, 3, 2, 2, 2, 74, 75, 12, 3, 2, 2, 75, 76, 7, 15, 2, 2, 76, 78, 5, 8, 5, 2, 77, 74, 3, 2, 2, 2, 78, 81, 3, 2, 2, 2, 79, 77, 3, 2, 2, 2, 79, 80, 3, 2, 2, 2, 80, 5, 3, 2, 2, 2, 81, 79, 3, 2, 2, 2, 82, 86, 5, 64, 33, 2, 83, 86, 5, 26, 14, 2, 84, 86, 5, 20, 11, 2, 85, 82, 3, 2, 2, 2, 85, 83, 3, 2, 2, 2, 85, 84, 3, 2, 2, 2, 86, 7, 3, 2, 2, 2, 87, 94, 5, 28, 15, 2, 88, 94, 5, 46, 24, 2, 89, 94, 5, 52, 27, 2, 90, 94, 5, 48, 25, 2, 91, 94, 5, 30, 16, 2, 92, 94, 5, 10, 6, 2, 93, 87, 3, 2, 2, 2, 93, 88, 3, 2, 2, 2, 93, 89, 3, 2, 2, 2, 93, 90, 3, 2, 2, 2, 93, 91, 3, 2, 2, 2, 93, 92, 3, 2, 2, 2, 94, 9, 3, 2, 2, 2, 95, 96, 7, 8, 2, 2, 96, 97, 5, 12, 7, 2, 97, 11, 3, 2, 2, 2, 98, 99, 8, 7, 1, 2, 99, 100, 7, 32, 2, 2, 100, 103, 5, 12, 7, 6, 101, 103, 5, 14, 8, 2, 102, 98, 3, 2, 2, 2, 102, 101, 3, 2, 2, 2, 103, 112, 3, 2, 2, 2, 104, 105, 12, 4, 2, 2, 105, 106, 7, 20, 2, 2, 106, 111, 5, 12, 7, 5, 107, 108, 12, 3, 2, 2, 108, 109, 7, 35, 2, 2, 109, 111, 5, 12, 7, 4, 110, 104, 3, 2, 2, 2, 110, 107, 3, 2, 2, 2, 111, 114, 3, 2, 2, 2, 112, 110, 3, 2, 2, 2, 112, 113, 3, 2, 2, 2, 113, 13, 3, 2, 2, 2, 114, 112, 3, 2, 2, 2, 115, 116, 5, 42, 22, 2, 116, 125, 7, 29, 2, 2, 117, 122, 5, 34, 18, 2, 118, 119, 7, 23, 2, 2, 119, 121, 5, 34, 18, 2, 120, 118, 3, 2, 2, 2, 121, 124, 3, 2, 2, 2, 122, 120, 3, 2, 2, 2, 122, 123, 3, 2, 2, 2, 123, 126, 3, 2, 2, 2, 124, 122, 3, 2, 2, 2, 125, 117, 3, 2, 2, 2, 125, 126, 3, 2, 2, 2, 126, 127, 3, 2, 2, 2, 127, 128, 7, 36, 2, 2, 128, 135, 3, 2, 2, 2, 129, 135, 5, 16, 9, 2, 130, 131, 5, 16, 9, 2, 131, 132, 5, 62, 32, 2, 132, 133, 5, 16, 9, 2, 133, 135, 3, 2, 2, 2, 134, 115, 3, 2, 2, 2, 134, 129, 3, 2, 2, 2, 134, 130, 3, 2, 2, 2, 135, 15, 3, 2, 2, 2, 136, 137, 8, 9, 1, 2, 137, 141, 5, 18, 10, 2, 138, 139, 9, 2, 2, 2, 139, 141, 5, 16, 9, 5, 140, 136, 3, 2, 2, 2, 140, 138, 3, 2, 2, 2, 141, 150, 3, 2, 2, 2, 142, 143, 12, 4, 2, 2, 143, 144, 9, 3, 2, 2, 144, 149, 5, 16, 9, 5, 145, 146, 12, 3, 2, 2, 146, 147, 9, 2, 2, 2, 147, 149, 5, 16, 9, 4, 148, 142, 3, 2, 2, 2, 148, 145, 3, 2, 2, 2, 149, 152, 3, 2, 2, 2, 150, 148, 3, 2, 2, 2, 150, 151, 3, 2, 2, 2, 151, 17, 3, 2, 2, 2, 152, 150, 3, 2, 2, 2, 153, 174, 5, 44, 23, 2, 154, 174, 5, 36, 19, 2, 155, 156, 7, 29, 2, 2, 156, 157, 5, 12, 7, 2, 157, 158, 7, 36, 2, 2, 158, 174, 3, 2, 2, 2, 159, 160, 5, 40, 21, 2, 160, 169, 7, 29, 2, 2, 161, 166, 5, 12, 7, 2, 162, 163, 7, 23, 2, 2, 163, 165, 5, 12, 7, 2, 164, 162, 3, 2, 2, 2, 165, 168, 3, 2, 2, 2, 166, 164, 3, 2, 2, 2, 166, 167, 3, 2, 2, 2, 167, 170, 3, 2, 2, 2, 168, 166, 3, 2, 2, 2, 169, 161, 3, 2, 2, 2, 169, 170, 3, 2, 2, 2, 170, 171, 3, 2, 2, 2, 171, 172, 7, 36, 2, 2, 172, 174, 3, 2, 2, 2, 173, 153, 3, 2, 2, 2, 173, 154, 3, 2, 2, 2, 173, 155, 3, 2, 2, 2, 173, 159, 3, 2, 2, 2, 174, 19, 3, 2, 2, 2, 175, 176, 7, 6, 2, 2, 176, 177, 5, 22, 12, 2, 177, 21, 3, 2, 2, 2, 178, 183, 5, 24, 13, 2, 179, 180, 7, 23, 2, 2, 180, 182, 5, 24, 13, 2, 181, 179, 3, 2, 2, 2, 182, 185, 3, 2, 2, 2, 183, 181, 3, 2, 2, 2, 183, 184, 3, 2, 2, 2, 184, 23, 3, 2, 2, 2, 185, 183, 3, 2, 2, 2, 186, 187, 5, 36, 19, 2, 187, 188, 7, 22, 2, 2, 188, 189, 5, 14, 8, 2, 189, 196, 3, 2, 2, 2, 190, 196, 5, 12, 7, 2, 191, 192, 5, 36, 19, 2, 192, 193, 7, 22, 2, 2, 193, 194, 5, 12, 7, 2, 194, 196, 3, 2, 2, 2, 195, 186, 3, 2, 2, 2, 195, 190, 3, 2, 2, 2, 195, 191, 3, 2, 2, 2, 196, 25, 3, 2, 2, 2, 197, 198, 7, 5, 2, 2, 198, 203, 5, 32, 17, 2, 199, 200, 7, 23, 2, 2, 200, 202, 5, 32, 17, 2, 201, 199, 3, 2, 2, 2, 202, 205, 3, 2, 2, 2, 203, 201, 3, 2, 2, 2, 203, 204, 3, 2, 2, 2, 204, 27, 3, 2, 2, 2, 205, 203, 3, 2, 2, 2, 206, 207, 7, 3, 2, 2, 207, 208, 5, 22, 12, 2, 208, 29, 3, 2, 2, 2, 209, 210, 7, 7, 2, 2, 210, 213, 5, 22, 12, 2, 211, 212, 7, 19, 2, 2, 212, 214, 5, 38, 20, 2, 213, 211, 3, 2, 2, 2, 213, 214, 3, 2, 2, 2, 214, 31, 3, 2, 2, 2, 215, 216, 9, 4, 2, 2, 216, 33, 3, 2, 2, 2, 217, 220, 5, 36, 19, 2, 218, 220, 5, 60, 31, 2, 219, 217, 3, 2, 2, 2, 219, 218, 3, 2, 2, 2, 220, 35, 3, 2, 2, 2, 221, 226, 5, 40, 21, 2, 222, 223, 7, 25, 2, 2, 223, 225, 5, 40, 21, 2, 224, 222, 3, 2, 2, 2, 225, 228, 3, 2, 2, 2, 226, 224, 3, 2, 2, 2, 226, 227, 3, 2, 2, 2, 227, 37, 3, 2, 2, 2, 228, 226, 3, 2, 2, 2, 229, 234, 5, 36, 19, 2, 230, 231, 7, 23, 2, 2, 231, 233, 5, 36, 19, 2, 232, 230, 3, 2, 2, 2, 233, 236, 3, 2, 2, 2, 234, 232, 3, 2, 2, 2, 234, 235, 3, 2, 2, 2, 235, 39, 3, 2, 2, 2, 236, 234, 3, 2, 2, 2, 237, 238, 9, 5, 2, 2, 238, 41, 3, 2, 2, 2, 239, 240, 9, 6, 2, 2, 240, 43, 3, 2, 2, 2, 241, 246, 7, 33, 2, 2, 242, 246, 5, 58, 30, 2, 243, 246, 5, 56, 29, 2, 244, 246, 5, 60, 31, 2, 245, 241, 3, 2, 2, 2, 245, 242, 3, 2, 2, 2, 245, 243, 3, 2, 2, 2, 245, 244, 3, 2, 2, 2, 246, 45, 3, 2, 2, 2, 247, 248, 7, 10, 2, 2, 248, 249, 7, 17, 2, 2, 249, 47, 3, 2, 2, 2, 250, 251, 7, 9, 2, 2, 251, 256, 5, 50, 26, 2, 252, 253, 7, 23, 2, 2, 253, 255, 5, 50, 26, 2, 254, 252, 3, 2, 2, 2, 255, 258, 3, 2, 2, 2, 256, 254, 3, 2, 2, 2, 256, 257, 3, 2, 2, 2, 257, 49, 3, 2, 2, 2, 258, 256, 3, 2, 2, 2, 259, 261, 5, 12, 7, 2, 260, 262, 9, 7, 2, 2, 261, 260, 3, 2, 2, 2, 261, 262, 3, 2, 2, 2, 262, 265, 3, 2, 2, 2, 263, 264, 7, 34, 2, 2, 264, 266, 9, 8, 2, 2, 265, 263, 3, 2, 2, 2, 265, 266, 3, 2, 2, 2, 266, 51, 3, 2, 2, 2, 267, 268, 7, 11, 2, 2, 268, 273, 5, 54, 28, 2, 269, 270, 7, 23, 2, 2, 270, 272, 5, 54, 28, 2, 271, 269, 3, 2, 2, 2, 272, 275, 3, 2, 2, 2, 273, 271, 3, 2, 2, 2, 273, 274, 3, 2, 2, 2, 274, 53, 3, 2, 2, 2, 275, 273, 3, 2, 2, 2, 276, 282, 5, 32, 17, 2, 277, 278, 5, 32, 17, 2, 278, 279, 7, 22, 2, 2, 279, 280, 5, 32, 17, 2, 280, 282, 3, 2, 2, 2, 281, 276, 3, 2, 2, 2, 281, 277, 3, 2, 2, 2, 282, 55, 3, 2, 2, 2, 283, 284, 9, 9, 2, 2, 284, 57, 3, 2, 2, 2, 285, 288, 7, 18, 2, 2, 286, 288, 7, 17, 2, 2, 287, 285, 3, 2, 2, 2, 287, 286, 3, 2, 2, 2, 288, 59, 3, 2, 2, 2, 289, 290, 7, 16, 2, 2, 290, 61, 3, 2, 2, 2, 291, 292, 9, 10, 2, 2, 292, 63, 3, 2, 2, 2, 293, 294, 7, 4, 2, 2, 294, 295, 5, 66, 34, 2, 295, 65, 3, 2, 2, 2, 296, 297, 7, 30, 2, 2, 297, 298, 5, 4, 3, 2, 298, 299, 7, 31, 2, 2, 299, 67, 3, 2, 2, 2, 31, 79, 85, 93, 102, 110, 112, 122, 125, 134, 140, 148, 150, 166, 169, 173, 183, 195, 203, 213, 219, 226, 234, 245, 256, 261, 265, 273, 281, 287] \ No newline at end of file +[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 3, 51, 307, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 84, 10, 3, 12, 3, 14, 3, 87, 11, 3, 3, 4, 3, 4, 3, 4, 5, 4, 92, 10, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 5, 5, 100, 10, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 109, 10, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 7, 7, 117, 10, 7, 12, 7, 14, 7, 120, 11, 7, 3, 8, 3, 8, 5, 8, 124, 10, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 135, 10, 10, 12, 10, 14, 10, 138, 11, 10, 5, 10, 140, 10, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 149, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 7, 11, 157, 10, 11, 12, 11, 14, 11, 160, 11, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 7, 12, 173, 10, 12, 12, 12, 14, 12, 176, 11, 12, 5, 12, 178, 10, 12, 3, 12, 3, 12, 5, 12, 182, 10, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 7, 14, 190, 10, 14, 12, 14, 14, 14, 193, 11, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 200, 10, 15, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 7, 17, 208, 10, 17, 12, 17, 14, 17, 211, 11, 17, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 5, 19, 220, 10, 19, 3, 20, 3, 20, 3, 21, 3, 21, 5, 21, 226, 10, 21, 3, 22, 3, 22, 3, 22, 7, 22, 231, 10, 22, 12, 22, 14, 22, 234, 11, 22, 3, 23, 3, 23, 3, 23, 7, 23, 239, 10, 23, 12, 23, 14, 23, 242, 11, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 5, 26, 252, 10, 26, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 261, 10, 28, 12, 28, 14, 28, 264, 11, 28, 3, 29, 3, 29, 5, 29, 268, 10, 29, 3, 29, 3, 29, 5, 29, 272, 10, 29, 3, 30, 3, 30, 3, 30, 3, 30, 7, 30, 278, 10, 30, 12, 30, 14, 30, 281, 11, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 31, 5, 31, 288, 10, 31, 3, 32, 3, 32, 3, 33, 3, 33, 5, 33, 294, 10, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 2, 2, 5, 4, 12, 20, 38, 2, 2, 4, 2, 6, 2, 8, 2, 10, 2, 12, 2, 14, 2, 16, 2, 18, 2, 20, 2, 22, 2, 24, 2, 26, 2, 28, 2, 30, 2, 32, 2, 34, 2, 36, 2, 38, 2, 40, 2, 42, 2, 44, 2, 46, 2, 48, 2, 50, 2, 52, 2, 54, 2, 56, 2, 58, 2, 60, 2, 62, 2, 64, 2, 66, 2, 68, 2, 70, 2, 72, 2, 2, 6, 3, 2, 33, 34, 3, 2, 35, 37, 3, 2, 47, 48, 3, 2, 42, 43, 2, 309, 2, 74, 3, 2, 2, 2, 4, 77, 3, 2, 2, 2, 6, 91, 3, 2, 2, 2, 8, 99, 3, 2, 2, 2, 10, 101, 3, 2, 2, 2, 12, 108, 3, 2, 2, 2, 14, 123, 3, 2, 2, 2, 16, 125, 3, 2, 2, 2, 18, 129, 3, 2, 2, 2, 20, 148, 3, 2, 2, 2, 22, 181, 3, 2, 2, 2, 24, 183, 3, 2, 2, 2, 26, 186, 3, 2, 2, 2, 28, 199, 3, 2, 2, 2, 30, 201, 3, 2, 2, 2, 32, 203, 3, 2, 2, 2, 34, 212, 3, 2, 2, 2, 36, 215, 3, 2, 2, 2, 38, 221, 3, 2, 2, 2, 40, 225, 3, 2, 2, 2, 42, 227, 3, 2, 2, 2, 44, 235, 3, 2, 2, 2, 46, 243, 3, 2, 2, 2, 48, 245, 3, 2, 2, 2, 50, 251, 3, 2, 2, 2, 52, 253, 3, 2, 2, 2, 54, 256, 3, 2, 2, 2, 56, 265, 3, 2, 2, 2, 58, 273, 3, 2, 2, 2, 60, 287, 3, 2, 2, 2, 62, 289, 3, 2, 2, 2, 64, 293, 3, 2, 2, 2, 66, 295, 3, 2, 2, 2, 68, 297, 3, 2, 2, 2, 70, 299, 3, 2, 2, 2, 72, 302, 3, 2, 2, 2, 74, 75, 5, 4, 3, 2, 75, 76, 7, 2, 2, 3, 76, 3, 3, 2, 2, 2, 77, 78, 8, 3, 1, 2, 78, 79, 5, 6, 4, 2, 79, 85, 3, 2, 2, 2, 80, 81, 12, 3, 2, 2, 81, 82, 7, 15, 2, 2, 82, 84, 5, 8, 5, 2, 83, 80, 3, 2, 2, 2, 84, 87, 3, 2, 2, 2, 85, 83, 3, 2, 2, 2, 85, 86, 3, 2, 2, 2, 86, 5, 3, 2, 2, 2, 87, 85, 3, 2, 2, 2, 88, 92, 5, 70, 36, 2, 89, 92, 5, 32, 17, 2, 90, 92, 5, 24, 13, 2, 91, 88, 3, 2, 2, 2, 91, 89, 3, 2, 2, 2, 91, 90, 3, 2, 2, 2, 92, 7, 3, 2, 2, 2, 93, 100, 5, 34, 18, 2, 94, 100, 5, 52, 27, 2, 95, 100, 5, 58, 30, 2, 96, 100, 5, 54, 28, 2, 97, 100, 5, 36, 19, 2, 98, 100, 5, 10, 6, 2, 99, 93, 3, 2, 2, 2, 99, 94, 3, 2, 2, 2, 99, 95, 3, 2, 2, 2, 99, 96, 3, 2, 2, 2, 99, 97, 3, 2, 2, 2, 99, 98, 3, 2, 2, 2, 100, 9, 3, 2, 2, 2, 101, 102, 7, 8, 2, 2, 102, 103, 5, 12, 7, 2, 103, 11, 3, 2, 2, 2, 104, 105, 8, 7, 1, 2, 105, 106, 7, 27, 2, 2, 106, 109, 5, 12, 7, 6, 107, 109, 5, 14, 8, 2, 108, 104, 3, 2, 2, 2, 108, 107, 3, 2, 2, 2, 109, 118, 3, 2, 2, 2, 110, 111, 12, 4, 2, 2, 111, 112, 7, 20, 2, 2, 112, 117, 5, 12, 7, 5, 113, 114, 12, 3, 2, 2, 114, 115, 7, 29, 2, 2, 115, 117, 5, 12, 7, 4, 116, 110, 3, 2, 2, 2, 116, 113, 3, 2, 2, 2, 117, 120, 3, 2, 2, 2, 118, 116, 3, 2, 2, 2, 118, 119, 3, 2, 2, 2, 119, 13, 3, 2, 2, 2, 120, 118, 3, 2, 2, 2, 121, 124, 5, 20, 11, 2, 122, 124, 5, 16, 9, 2, 123, 121, 3, 2, 2, 2, 123, 122, 3, 2, 2, 2, 124, 15, 3, 2, 2, 2, 125, 126, 5, 20, 11, 2, 126, 127, 5, 68, 35, 2, 127, 128, 5, 20, 11, 2, 128, 17, 3, 2, 2, 2, 129, 130, 5, 48, 25, 2, 130, 139, 7, 24, 2, 2, 131, 136, 5, 40, 21, 2, 132, 133, 7, 22, 2, 2, 133, 135, 5, 40, 21, 2, 134, 132, 3, 2, 2, 2, 135, 138, 3, 2, 2, 2, 136, 134, 3, 2, 2, 2, 136, 137, 3, 2, 2, 2, 137, 140, 3, 2, 2, 2, 138, 136, 3, 2, 2, 2, 139, 131, 3, 2, 2, 2, 139, 140, 3, 2, 2, 2, 140, 141, 3, 2, 2, 2, 141, 142, 7, 30, 2, 2, 142, 19, 3, 2, 2, 2, 143, 144, 8, 11, 1, 2, 144, 149, 5, 22, 12, 2, 145, 149, 5, 18, 10, 2, 146, 147, 9, 2, 2, 2, 147, 149, 5, 20, 11, 5, 148, 143, 3, 2, 2, 2, 148, 145, 3, 2, 2, 2, 148, 146, 3, 2, 2, 2, 149, 158, 3, 2, 2, 2, 150, 151, 12, 4, 2, 2, 151, 152, 9, 3, 2, 2, 152, 157, 5, 20, 11, 5, 153, 154, 12, 3, 2, 2, 154, 155, 9, 2, 2, 2, 155, 157, 5, 20, 11, 4, 156, 150, 3, 2, 2, 2, 156, 153, 3, 2, 2, 2, 157, 160, 3, 2, 2, 2, 158, 156, 3, 2, 2, 2, 158, 159, 3, 2, 2, 2, 159, 21, 3, 2, 2, 2, 160, 158, 3, 2, 2, 2, 161, 182, 5, 50, 26, 2, 162, 182, 5, 42, 22, 2, 163, 164, 7, 24, 2, 2, 164, 165, 5, 12, 7, 2, 165, 166, 7, 30, 2, 2, 166, 182, 3, 2, 2, 2, 167, 168, 5, 46, 24, 2, 168, 177, 7, 24, 2, 2, 169, 174, 5, 12, 7, 2, 170, 171, 7, 22, 2, 2, 171, 173, 5, 12, 7, 2, 172, 170, 3, 2, 2, 2, 173, 176, 3, 2, 2, 2, 174, 172, 3, 2, 2, 2, 174, 175, 3, 2, 2, 2, 175, 178, 3, 2, 2, 2, 176, 174, 3, 2, 2, 2, 177, 169, 3, 2, 2, 2, 177, 178, 3, 2, 2, 2, 178, 179, 3, 2, 2, 2, 179, 180, 7, 30, 2, 2, 180, 182, 3, 2, 2, 2, 181, 161, 3, 2, 2, 2, 181, 162, 3, 2, 2, 2, 181, 163, 3, 2, 2, 2, 181, 167, 3, 2, 2, 2, 182, 23, 3, 2, 2, 2, 183, 184, 7, 6, 2, 2, 184, 185, 5, 26, 14, 2, 185, 25, 3, 2, 2, 2, 186, 191, 5, 28, 15, 2, 187, 188, 7, 22, 2, 2, 188, 190, 5, 28, 15, 2, 189, 187, 3, 2, 2, 2, 190, 193, 3, 2, 2, 2, 191, 189, 3, 2, 2, 2, 191, 192, 3, 2, 2, 2, 192, 27, 3, 2, 2, 2, 193, 191, 3, 2, 2, 2, 194, 200, 5, 12, 7, 2, 195, 196, 5, 30, 16, 2, 196, 197, 7, 21, 2, 2, 197, 198, 5, 12, 7, 2, 198, 200, 3, 2, 2, 2, 199, 194, 3, 2, 2, 2, 199, 195, 3, 2, 2, 2, 200, 29, 3, 2, 2, 2, 201, 202, 5, 46, 24, 2, 202, 31, 3, 2, 2, 2, 203, 204, 7, 5, 2, 2, 204, 209, 5, 38, 20, 2, 205, 206, 7, 22, 2, 2, 206, 208, 5, 38, 20, 2, 207, 205, 3, 2, 2, 2, 208, 211, 3, 2, 2, 2, 209, 207, 3, 2, 2, 2, 209, 210, 3, 2, 2, 2, 210, 33, 3, 2, 2, 2, 211, 209, 3, 2, 2, 2, 212, 213, 7, 3, 2, 2, 213, 214, 5, 26, 14, 2, 214, 35, 3, 2, 2, 2, 215, 216, 7, 7, 2, 2, 216, 219, 5, 26, 14, 2, 217, 218, 7, 19, 2, 2, 218, 220, 5, 44, 23, 2, 219, 217, 3, 2, 2, 2, 219, 220, 3, 2, 2, 2, 220, 37, 3, 2, 2, 2, 221, 222, 9, 4, 2, 2, 222, 39, 3, 2, 2, 2, 223, 226, 5, 42, 22, 2, 224, 226, 5, 66, 34, 2, 225, 223, 3, 2, 2, 2, 225, 224, 3, 2, 2, 2, 226, 41, 3, 2, 2, 2, 227, 232, 5, 46, 24, 2, 228, 229, 7, 23, 2, 2, 229, 231, 5, 46, 24, 2, 230, 228, 3, 2, 2, 2, 231, 234, 3, 2, 2, 2, 232, 230, 3, 2, 2, 2, 232, 233, 3, 2, 2, 2, 233, 43, 3, 2, 2, 2, 234, 232, 3, 2, 2, 2, 235, 240, 5, 42, 22, 2, 236, 237, 7, 22, 2, 2, 237, 239, 5, 42, 22, 2, 238, 236, 3, 2, 2, 2, 239, 242, 3, 2, 2, 2, 240, 238, 3, 2, 2, 2, 240, 241, 3, 2, 2, 2, 241, 45, 3, 2, 2, 2, 242, 240, 3, 2, 2, 2, 243, 244, 9, 5, 2, 2, 244, 47, 3, 2, 2, 2, 245, 246, 7, 41, 2, 2, 246, 49, 3, 2, 2, 2, 247, 252, 7, 28, 2, 2, 248, 252, 5, 64, 33, 2, 249, 252, 5, 62, 32, 2, 250, 252, 5, 66, 34, 2, 251, 247, 3, 2, 2, 2, 251, 248, 3, 2, 2, 2, 251, 249, 3, 2, 2, 2, 251, 250, 3, 2, 2, 2, 252, 51, 3, 2, 2, 2, 253, 254, 7, 10, 2, 2, 254, 255, 7, 17, 2, 2, 255, 53, 3, 2, 2, 2, 256, 257, 7, 9, 2, 2, 257, 262, 5, 56, 29, 2, 258, 259, 7, 22, 2, 2, 259, 261, 5, 56, 29, 2, 260, 258, 3, 2, 2, 2, 261, 264, 3, 2, 2, 2, 262, 260, 3, 2, 2, 2, 262, 263, 3, 2, 2, 2, 263, 55, 3, 2, 2, 2, 264, 262, 3, 2, 2, 2, 265, 267, 5, 12, 7, 2, 266, 268, 7, 38, 2, 2, 267, 266, 3, 2, 2, 2, 267, 268, 3, 2, 2, 2, 268, 271, 3, 2, 2, 2, 269, 270, 7, 39, 2, 2, 270, 272, 7, 40, 2, 2, 271, 269, 3, 2, 2, 2, 271, 272, 3, 2, 2, 2, 272, 57, 3, 2, 2, 2, 273, 274, 7, 11, 2, 2, 274, 279, 5, 60, 31, 2, 275, 276, 7, 22, 2, 2, 276, 278, 5, 60, 31, 2, 277, 275, 3, 2, 2, 2, 278, 281, 3, 2, 2, 2, 279, 277, 3, 2, 2, 2, 279, 280, 3, 2, 2, 2, 280, 59, 3, 2, 2, 2, 281, 279, 3, 2, 2, 2, 282, 288, 5, 38, 20, 2, 283, 284, 5, 38, 20, 2, 284, 285, 7, 21, 2, 2, 285, 286, 5, 38, 20, 2, 286, 288, 3, 2, 2, 2, 287, 282, 3, 2, 2, 2, 287, 283, 3, 2, 2, 2, 288, 61, 3, 2, 2, 2, 289, 290, 7, 31, 2, 2, 290, 63, 3, 2, 2, 2, 291, 294, 7, 18, 2, 2, 292, 294, 7, 17, 2, 2, 293, 291, 3, 2, 2, 2, 293, 292, 3, 2, 2, 2, 294, 65, 3, 2, 2, 2, 295, 296, 7, 16, 2, 2, 296, 67, 3, 2, 2, 2, 297, 298, 7, 32, 2, 2, 298, 69, 3, 2, 2, 2, 299, 300, 7, 4, 2, 2, 300, 301, 5, 72, 37, 2, 301, 71, 3, 2, 2, 2, 302, 303, 7, 25, 2, 2, 303, 304, 5, 4, 3, 2, 304, 305, 7, 26, 2, 2, 305, 73, 3, 2, 2, 2, 31, 85, 91, 99, 108, 116, 118, 123, 136, 139, 148, 156, 158, 174, 177, 181, 191, 199, 209, 219, 225, 232, 240, 251, 262, 267, 271, 279, 287, 293] \ No newline at end of file diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens b/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens index b39004ce4ce3..c2dafff2f222 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens @@ -16,50 +16,37 @@ INTEGER_LITERAL=15 DECIMAL_LITERAL=16 BY=17 AND=18 -ASC=19 -ASSIGN=20 -COMMA=21 -DESC=22 -DOT=23 -FALSE=24 -FIRST=25 -LAST=26 -LP=27 -OPENING_BRACKET=28 -CLOSING_BRACKET=29 -NOT=30 -NULL=31 -NULLS=32 -OR=33 -RP=34 -TRUE=35 -EQ=36 -NEQ=37 -LT=38 -LTE=39 -GT=40 -GTE=41 -PLUS=42 -MINUS=43 -ASTERISK=44 -SLASH=45 -PERCENT=46 -ROUND_FUNCTION_MATH=47 -AVG_FUNCTION_MATH=48 -SUM_FUNCTION_MATH=49 -MIN_FUNCTION_MATH=50 -MAX_FUNCTION_MATH=51 -UNQUOTED_IDENTIFIER=52 -QUOTED_IDENTIFIER=53 -EXPR_LINE_COMMENT=54 -EXPR_MULTILINE_COMMENT=55 -EXPR_WS=56 -SRC_UNQUOTED_IDENTIFIER=57 -SRC_QUOTED_IDENTIFIER=58 -SRC_LINE_COMMENT=59 -SRC_MULTILINE_COMMENT=60 -SRC_WS=61 -UNKNOWN_CMD=62 +ASSIGN=19 +COMMA=20 +DOT=21 +LP=22 +OPENING_BRACKET=23 +CLOSING_BRACKET=24 +NOT=25 +NULL=26 +OR=27 +RP=28 +BOOLEAN_VALUE=29 +COMPARISON_OPERATOR=30 +PLUS=31 +MINUS=32 +ASTERISK=33 +SLASH=34 +PERCENT=35 +ORDERING=36 +NULLS_ORDERING=37 +NULLS_ORDERING_DIRECTION=38 +UNARY_FUNCTION=39 +UNQUOTED_IDENTIFIER=40 +QUOTED_IDENTIFIER=41 +EXPR_LINE_COMMENT=42 +EXPR_MULTILINE_COMMENT=43 +EXPR_WS=44 +SRC_UNQUOTED_IDENTIFIER=45 +SRC_QUOTED_IDENTIFIER=46 +SRC_LINE_COMMENT=47 +SRC_MULTILINE_COMMENT=48 +SRC_WS=49 'eval'=1 'explain'=2 'from'=3 @@ -71,34 +58,17 @@ UNKNOWN_CMD=62 'project'=9 'by'=17 'and'=18 -'asc'=19 -'desc'=22 -'.'=23 -'false'=24 -'first'=25 -'last'=26 -'('=27 -'['=28 -']'=29 -'not'=30 -'null'=31 -'nulls'=32 -'or'=33 -')'=34 -'true'=35 -'=='=36 -'!='=37 -'<'=38 -'<='=39 -'>'=40 -'>='=41 -'+'=42 -'-'=43 -'*'=44 -'/'=45 -'%'=46 -'round'=47 -'avg'=48 -'sum'=49 -'min'=50 -'max'=51 +'.'=21 +'('=22 +'['=23 +']'=24 +'not'=25 +'null'=26 +'or'=27 +')'=28 +'+'=31 +'-'=32 +'*'=33 +'/'=34 +'%'=35 +'nulls'=37 diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.ts b/packages/kbn-monaco/src/esql/antlr/esql_parser.ts index e913ccde441b..de825a0b3698 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.ts +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.ts @@ -45,50 +45,37 @@ export class esql_parser extends Parser { public static readonly DECIMAL_LITERAL = 16; public static readonly BY = 17; public static readonly AND = 18; - public static readonly ASC = 19; - public static readonly ASSIGN = 20; - public static readonly COMMA = 21; - public static readonly DESC = 22; - public static readonly DOT = 23; - public static readonly FALSE = 24; - public static readonly FIRST = 25; - public static readonly LAST = 26; - public static readonly LP = 27; - public static readonly OPENING_BRACKET = 28; - public static readonly CLOSING_BRACKET = 29; - public static readonly NOT = 30; - public static readonly NULL = 31; - public static readonly NULLS = 32; - public static readonly OR = 33; - public static readonly RP = 34; - public static readonly TRUE = 35; - public static readonly EQ = 36; - public static readonly NEQ = 37; - public static readonly LT = 38; - public static readonly LTE = 39; - public static readonly GT = 40; - public static readonly GTE = 41; - public static readonly PLUS = 42; - public static readonly MINUS = 43; - public static readonly ASTERISK = 44; - public static readonly SLASH = 45; - public static readonly PERCENT = 46; - public static readonly ROUND_FUNCTION_MATH = 47; - public static readonly AVG_FUNCTION_MATH = 48; - public static readonly SUM_FUNCTION_MATH = 49; - public static readonly MIN_FUNCTION_MATH = 50; - public static readonly MAX_FUNCTION_MATH = 51; - public static readonly UNQUOTED_IDENTIFIER = 52; - public static readonly QUOTED_IDENTIFIER = 53; - public static readonly EXPR_LINE_COMMENT = 54; - public static readonly EXPR_MULTILINE_COMMENT = 55; - public static readonly EXPR_WS = 56; - public static readonly SRC_UNQUOTED_IDENTIFIER = 57; - public static readonly SRC_QUOTED_IDENTIFIER = 58; - public static readonly SRC_LINE_COMMENT = 59; - public static readonly SRC_MULTILINE_COMMENT = 60; - public static readonly SRC_WS = 61; - public static readonly UNKNOWN_CMD = 62; + public static readonly ASSIGN = 19; + public static readonly COMMA = 20; + public static readonly DOT = 21; + public static readonly LP = 22; + public static readonly OPENING_BRACKET = 23; + public static readonly CLOSING_BRACKET = 24; + public static readonly NOT = 25; + public static readonly NULL = 26; + public static readonly OR = 27; + public static readonly RP = 28; + public static readonly BOOLEAN_VALUE = 29; + public static readonly COMPARISON_OPERATOR = 30; + public static readonly PLUS = 31; + public static readonly MINUS = 32; + public static readonly ASTERISK = 33; + public static readonly SLASH = 34; + public static readonly PERCENT = 35; + public static readonly ORDERING = 36; + public static readonly NULLS_ORDERING = 37; + public static readonly NULLS_ORDERING_DIRECTION = 38; + public static readonly UNARY_FUNCTION = 39; + public static readonly UNQUOTED_IDENTIFIER = 40; + public static readonly QUOTED_IDENTIFIER = 41; + public static readonly EXPR_LINE_COMMENT = 42; + public static readonly EXPR_MULTILINE_COMMENT = 43; + public static readonly EXPR_WS = 44; + public static readonly SRC_UNQUOTED_IDENTIFIER = 45; + public static readonly SRC_QUOTED_IDENTIFIER = 46; + public static readonly SRC_LINE_COMMENT = 47; + public static readonly SRC_MULTILINE_COMMENT = 48; + public static readonly SRC_WS = 49; public static readonly RULE_singleStatement = 0; public static readonly RULE_query = 1; public static readonly RULE_sourceCommand = 2; @@ -96,63 +83,64 @@ export class esql_parser extends Parser { public static readonly RULE_whereCommand = 4; public static readonly RULE_booleanExpression = 5; public static readonly RULE_valueExpression = 6; - public static readonly RULE_operatorExpression = 7; - public static readonly RULE_primaryExpression = 8; - public static readonly RULE_rowCommand = 9; - public static readonly RULE_fields = 10; - public static readonly RULE_field = 11; - public static readonly RULE_fromCommand = 12; - public static readonly RULE_evalCommand = 13; - public static readonly RULE_statsCommand = 14; - public static readonly RULE_sourceIdentifier = 15; - public static readonly RULE_functionExpressionArgument = 16; - public static readonly RULE_qualifiedName = 17; - public static readonly RULE_qualifiedNames = 18; - public static readonly RULE_identifier = 19; - public static readonly RULE_functionIdentifier = 20; - public static readonly RULE_constant = 21; - public static readonly RULE_limitCommand = 22; - public static readonly RULE_sortCommand = 23; - public static readonly RULE_orderExpression = 24; - public static readonly RULE_projectCommand = 25; - public static readonly RULE_projectClause = 26; - public static readonly RULE_booleanValue = 27; - public static readonly RULE_number = 28; - public static readonly RULE_string = 29; - public static readonly RULE_comparisonOperator = 30; - public static readonly RULE_explainCommand = 31; - public static readonly RULE_subqueryExpression = 32; + public static readonly RULE_comparison = 7; + public static readonly RULE_mathFn = 8; + public static readonly RULE_operatorExpression = 9; + public static readonly RULE_primaryExpression = 10; + public static readonly RULE_rowCommand = 11; + public static readonly RULE_fields = 12; + public static readonly RULE_field = 13; + public static readonly RULE_userVariable = 14; + public static readonly RULE_fromCommand = 15; + public static readonly RULE_evalCommand = 16; + public static readonly RULE_statsCommand = 17; + public static readonly RULE_sourceIdentifier = 18; + public static readonly RULE_functionExpressionArgument = 19; + public static readonly RULE_qualifiedName = 20; + public static readonly RULE_qualifiedNames = 21; + public static readonly RULE_identifier = 22; + public static readonly RULE_functionIdentifier = 23; + public static readonly RULE_constant = 24; + public static readonly RULE_limitCommand = 25; + public static readonly RULE_sortCommand = 26; + public static readonly RULE_orderExpression = 27; + public static readonly RULE_projectCommand = 28; + public static readonly RULE_projectClause = 29; + public static readonly RULE_booleanValue = 30; + public static readonly RULE_number = 31; + public static readonly RULE_string = 32; + public static readonly RULE_comparisonOperator = 33; + public static readonly RULE_explainCommand = 34; + public static readonly RULE_subqueryExpression = 35; // tslint:disable:no-trailing-whitespace public static readonly ruleNames: string[] = [ "singleStatement", "query", "sourceCommand", "processingCommand", "whereCommand", - "booleanExpression", "valueExpression", "operatorExpression", "primaryExpression", - "rowCommand", "fields", "field", "fromCommand", "evalCommand", "statsCommand", - "sourceIdentifier", "functionExpressionArgument", "qualifiedName", "qualifiedNames", - "identifier", "functionIdentifier", "constant", "limitCommand", "sortCommand", - "orderExpression", "projectCommand", "projectClause", "booleanValue", - "number", "string", "comparisonOperator", "explainCommand", "subqueryExpression", + "booleanExpression", "valueExpression", "comparison", "mathFn", "operatorExpression", + "primaryExpression", "rowCommand", "fields", "field", "userVariable", + "fromCommand", "evalCommand", "statsCommand", "sourceIdentifier", "functionExpressionArgument", + "qualifiedName", "qualifiedNames", "identifier", "functionIdentifier", + "constant", "limitCommand", "sortCommand", "orderExpression", "projectCommand", + "projectClause", "booleanValue", "number", "string", "comparisonOperator", + "explainCommand", "subqueryExpression", ]; private static readonly _LITERAL_NAMES: Array = [ undefined, "'eval'", "'explain'", "'from'", "'row'", "'stats'", "'where'", "'sort'", "'limit'", "'project'", undefined, undefined, undefined, undefined, - undefined, undefined, undefined, "'by'", "'and'", "'asc'", undefined, - undefined, "'desc'", "'.'", "'false'", "'first'", "'last'", "'('", "'['", - "']'", "'not'", "'null'", "'nulls'", "'or'", "')'", "'true'", "'=='", - "'!='", "'<'", "'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", "'%'", - "'round'", "'avg'", "'sum'", "'min'", "'max'", + undefined, undefined, undefined, "'by'", "'and'", undefined, undefined, + "'.'", "'('", "'['", "']'", "'not'", "'null'", "'or'", "')'", undefined, + undefined, "'+'", "'-'", "'*'", "'/'", "'%'", undefined, "'nulls'", ]; private static readonly _SYMBOLIC_NAMES: Array = [ undefined, "EVAL", "EXPLAIN", "FROM", "ROW", "STATS", "WHERE", "SORT", "LIMIT", "PROJECT", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "PIPE", - "STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", - "COMMA", "DESC", "DOT", "FALSE", "FIRST", "LAST", "LP", "OPENING_BRACKET", - "CLOSING_BRACKET", "NOT", "NULL", "NULLS", "OR", "RP", "TRUE", "EQ", "NEQ", - "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", "ASTERISK", "SLASH", "PERCENT", - "ROUND_FUNCTION_MATH", "AVG_FUNCTION_MATH", "SUM_FUNCTION_MATH", "MIN_FUNCTION_MATH", - "MAX_FUNCTION_MATH", "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", + "STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASSIGN", + "COMMA", "DOT", "LP", "OPENING_BRACKET", "CLOSING_BRACKET", "NOT", "NULL", + "OR", "RP", "BOOLEAN_VALUE", "COMPARISON_OPERATOR", "PLUS", "MINUS", "ASTERISK", + "SLASH", "PERCENT", "ORDERING", "NULLS_ORDERING", "NULLS_ORDERING_DIRECTION", + "UNARY_FUNCTION", "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", "SRC_UNQUOTED_IDENTIFIER", "SRC_QUOTED_IDENTIFIER", - "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", "SRC_WS", "UNKNOWN_CMD", + "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", "SRC_WS", ]; public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(esql_parser._LITERAL_NAMES, esql_parser._SYMBOLIC_NAMES, []); @@ -183,9 +171,9 @@ export class esql_parser extends Parser { try { this.enterOuterAlt(_localctx, 1); { - this.state = 66; + this.state = 72; this.query(0); - this.state = 67; + this.state = 73; this.match(esql_parser.EOF); } } @@ -227,11 +215,11 @@ export class esql_parser extends Parser { this._ctx = _localctx; _prevctx = _localctx; - this.state = 70; + this.state = 76; this.sourceCommand(); } this._ctx._stop = this._input.tryLT(-1); - this.state = 77; + this.state = 83; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 0, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -244,18 +232,18 @@ export class esql_parser extends Parser { { _localctx = new CompositeQueryContext(new QueryContext(_parentctx, _parentState)); this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_query); - this.state = 72; + this.state = 78; if (!(this.precpred(this._ctx, 1))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 1)"); } - this.state = 73; + this.state = 79; this.match(esql_parser.PIPE); - this.state = 74; + this.state = 80; this.processingCommand(); } } } - this.state = 79; + this.state = 85; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 0, this._ctx); } @@ -280,27 +268,27 @@ export class esql_parser extends Parser { let _localctx: SourceCommandContext = new SourceCommandContext(this._ctx, this.state); this.enterRule(_localctx, 4, esql_parser.RULE_sourceCommand); try { - this.state = 83; + this.state = 89; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.EXPLAIN: this.enterOuterAlt(_localctx, 1); { - this.state = 80; + this.state = 86; this.explainCommand(); } break; case esql_parser.FROM: this.enterOuterAlt(_localctx, 2); { - this.state = 81; + this.state = 87; this.fromCommand(); } break; case esql_parser.ROW: this.enterOuterAlt(_localctx, 3); { - this.state = 82; + this.state = 88; this.rowCommand(); } break; @@ -327,48 +315,48 @@ export class esql_parser extends Parser { let _localctx: ProcessingCommandContext = new ProcessingCommandContext(this._ctx, this.state); this.enterRule(_localctx, 6, esql_parser.RULE_processingCommand); try { - this.state = 91; + this.state = 97; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.EVAL: this.enterOuterAlt(_localctx, 1); { - this.state = 85; + this.state = 91; this.evalCommand(); } break; case esql_parser.LIMIT: this.enterOuterAlt(_localctx, 2); { - this.state = 86; + this.state = 92; this.limitCommand(); } break; case esql_parser.PROJECT: this.enterOuterAlt(_localctx, 3); { - this.state = 87; + this.state = 93; this.projectCommand(); } break; case esql_parser.SORT: this.enterOuterAlt(_localctx, 4); { - this.state = 88; + this.state = 94; this.sortCommand(); } break; case esql_parser.STATS: this.enterOuterAlt(_localctx, 5); { - this.state = 89; + this.state = 95; this.statsCommand(); } break; case esql_parser.WHERE: this.enterOuterAlt(_localctx, 6); { - this.state = 90; + this.state = 96; this.whereCommand(); } break; @@ -397,9 +385,9 @@ export class esql_parser extends Parser { try { this.enterOuterAlt(_localctx, 1); { - this.state = 93; + this.state = 99; this.match(esql_parser.WHERE); - this.state = 94; + this.state = 100; this.booleanExpression(0); } } @@ -436,42 +424,30 @@ export class esql_parser extends Parser { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 100; + this.state = 106; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.NOT: { - _localctx = new LogicalNotContext(_localctx); - this._ctx = _localctx; - _prevctx = _localctx; - - this.state = 97; + this.state = 103; this.match(esql_parser.NOT); - this.state = 98; + this.state = 104; this.booleanExpression(4); } break; case esql_parser.STRING: case esql_parser.INTEGER_LITERAL: case esql_parser.DECIMAL_LITERAL: - case esql_parser.FALSE: case esql_parser.LP: case esql_parser.NULL: - case esql_parser.TRUE: + case esql_parser.BOOLEAN_VALUE: case esql_parser.PLUS: case esql_parser.MINUS: - case esql_parser.ROUND_FUNCTION_MATH: - case esql_parser.AVG_FUNCTION_MATH: - case esql_parser.SUM_FUNCTION_MATH: - case esql_parser.MIN_FUNCTION_MATH: - case esql_parser.MAX_FUNCTION_MATH: + case esql_parser.UNARY_FUNCTION: case esql_parser.UNQUOTED_IDENTIFIER: case esql_parser.QUOTED_IDENTIFIER: { - _localctx = new BooleanDefaultContext(_localctx); - this._ctx = _localctx; - _prevctx = _localctx; - this.state = 99; + this.state = 105; this.valueExpression(); } break; @@ -479,7 +455,7 @@ export class esql_parser extends Parser { throw new NoViableAltException(this); } this._ctx._stop = this._input.tryLT(-1); - this.state = 110; + this.state = 116; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 5, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -489,44 +465,44 @@ export class esql_parser extends Parser { } _prevctx = _localctx; { - this.state = 108; + this.state = 114; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 4, this._ctx) ) { case 1: { - _localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState)); - (_localctx as LogicalBinaryContext)._left = _prevctx; + _localctx = new BooleanExpressionContext(_parentctx, _parentState); + _localctx._left = _prevctx; this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 102; + this.state = 108; if (!(this.precpred(this._ctx, 2))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 2)"); } - this.state = 103; - (_localctx as LogicalBinaryContext)._operator = this.match(esql_parser.AND); - this.state = 104; - (_localctx as LogicalBinaryContext)._right = this.booleanExpression(3); + this.state = 109; + _localctx._operator = this.match(esql_parser.AND); + this.state = 110; + _localctx._right = this.booleanExpression(3); } break; case 2: { - _localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState)); - (_localctx as LogicalBinaryContext)._left = _prevctx; + _localctx = new BooleanExpressionContext(_parentctx, _parentState); + _localctx._left = _prevctx; this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 105; + this.state = 111; if (!(this.precpred(this._ctx, 1))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 1)"); } - this.state = 106; - (_localctx as LogicalBinaryContext)._operator = this.match(esql_parser.OR); - this.state = 107; - (_localctx as LogicalBinaryContext)._right = this.booleanExpression(2); + this.state = 112; + _localctx._operator = this.match(esql_parser.OR); + this.state = 113; + _localctx._right = this.booleanExpression(2); } break; } } } - this.state = 112; + this.state = 118; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 5, this._ctx); } @@ -550,71 +526,110 @@ export class esql_parser extends Parser { public valueExpression(): ValueExpressionContext { let _localctx: ValueExpressionContext = new ValueExpressionContext(this._ctx, this.state); this.enterRule(_localctx, 12, esql_parser.RULE_valueExpression); - let _la: number; try { - this.state = 132; + this.state = 121; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 8, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 6, this._ctx) ) { case 1: - _localctx = new ValueFunctionExpressionContext(_localctx); this.enterOuterAlt(_localctx, 1); { - this.state = 113; - this.functionIdentifier(); - this.state = 114; - this.match(esql_parser.LP); - this.state = 123; - this._errHandler.sync(this); - _la = this._input.LA(1); - if (_la === esql_parser.STRING || _la === esql_parser.UNQUOTED_IDENTIFIER || _la === esql_parser.QUOTED_IDENTIFIER) { - { - this.state = 115; - this.functionExpressionArgument(); - this.state = 120; - this._errHandler.sync(this); - _la = this._input.LA(1); - while (_la === esql_parser.COMMA) { - { - { - this.state = 116; - this.match(esql_parser.COMMA); - this.state = 117; - this.functionExpressionArgument(); - } - } - this.state = 122; - this._errHandler.sync(this); - _la = this._input.LA(1); - } - } - } - - this.state = 125; - this.match(esql_parser.RP); + this.state = 119; + this.operatorExpression(0); } break; case 2: - _localctx = new ValueExpressionDefaultContext(_localctx); this.enterOuterAlt(_localctx, 2); { - this.state = 127; - this.operatorExpression(0); + this.state = 120; + this.comparison(); } break; - - case 3: - _localctx = new ComparisonContext(_localctx); - this.enterOuterAlt(_localctx, 3); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + // @RuleVersion(0) + public comparison(): ComparisonContext { + let _localctx: ComparisonContext = new ComparisonContext(this._ctx, this.state); + this.enterRule(_localctx, 14, esql_parser.RULE_comparison); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 123; + _localctx._left = this.operatorExpression(0); + this.state = 124; + this.comparisonOperator(); + this.state = 125; + _localctx._right = this.operatorExpression(0); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + // @RuleVersion(0) + public mathFn(): MathFnContext { + let _localctx: MathFnContext = new MathFnContext(this._ctx, this.state); + this.enterRule(_localctx, 16, esql_parser.RULE_mathFn); + let _la: number; + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 127; + this.functionIdentifier(); + this.state = 128; + this.match(esql_parser.LP); + this.state = 137; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (((((_la - 14)) & ~0x1F) === 0 && ((1 << (_la - 14)) & ((1 << (esql_parser.STRING - 14)) | (1 << (esql_parser.UNQUOTED_IDENTIFIER - 14)) | (1 << (esql_parser.QUOTED_IDENTIFIER - 14)))) !== 0)) { { - this.state = 128; - (_localctx as ComparisonContext)._left = this.operatorExpression(0); this.state = 129; - this.comparisonOperator(); - this.state = 130; - (_localctx as ComparisonContext)._right = this.operatorExpression(0); + this.functionExpressionArgument(); + this.state = 134; + this._errHandler.sync(this); + _la = this._input.LA(1); + while (_la === esql_parser.COMMA) { + { + { + this.state = 130; + this.match(esql_parser.COMMA); + this.state = 131; + this.functionExpressionArgument(); + } + } + this.state = 136; + this._errHandler.sync(this); + _la = this._input.LA(1); } - break; + } + } + + this.state = 139; + this.match(esql_parser.RP); } } catch (re) { @@ -644,45 +659,43 @@ export class esql_parser extends Parser { let _parentState: number = this.state; let _localctx: OperatorExpressionContext = new OperatorExpressionContext(this._ctx, _parentState); let _prevctx: OperatorExpressionContext = _localctx; - let _startState: number = 14; - this.enterRecursionRule(_localctx, 14, esql_parser.RULE_operatorExpression, _p); + let _startState: number = 18; + this.enterRecursionRule(_localctx, 18, esql_parser.RULE_operatorExpression, _p); let _la: number; try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 138; + this.state = 146; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.STRING: case esql_parser.INTEGER_LITERAL: case esql_parser.DECIMAL_LITERAL: - case esql_parser.FALSE: case esql_parser.LP: case esql_parser.NULL: - case esql_parser.TRUE: + case esql_parser.BOOLEAN_VALUE: case esql_parser.UNQUOTED_IDENTIFIER: case esql_parser.QUOTED_IDENTIFIER: { - _localctx = new OperatorExpressionDefaultContext(_localctx); - this._ctx = _localctx; - _prevctx = _localctx; - - this.state = 135; + this.state = 142; this.primaryExpression(); } break; + case esql_parser.UNARY_FUNCTION: + { + this.state = 143; + this.mathFn(); + } + break; case esql_parser.PLUS: case esql_parser.MINUS: { - _localctx = new ArithmeticUnaryContext(_localctx); - this._ctx = _localctx; - _prevctx = _localctx; - this.state = 136; - (_localctx as ArithmeticUnaryContext)._operator = this._input.LT(1); + this.state = 144; + _localctx._operator = this._input.LT(1); _la = this._input.LA(1); if (!(_la === esql_parser.PLUS || _la === esql_parser.MINUS)) { - (_localctx as ArithmeticUnaryContext)._operator = this._errHandler.recoverInline(this); + _localctx._operator = this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { this.matchedEOF = true; @@ -691,7 +704,7 @@ export class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 137; + this.state = 145; this.operatorExpression(3); } break; @@ -699,7 +712,7 @@ export class esql_parser extends Parser { throw new NoViableAltException(this); } this._ctx._stop = this._input.tryLT(-1); - this.state = 148; + this.state = 156; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 11, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -709,23 +722,23 @@ export class esql_parser extends Parser { } _prevctx = _localctx; { - this.state = 146; + this.state = 154; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 10, this._ctx) ) { case 1: { - _localctx = new ArithmeticBinaryContext(new OperatorExpressionContext(_parentctx, _parentState)); - (_localctx as ArithmeticBinaryContext)._left = _prevctx; + _localctx = new OperatorExpressionContext(_parentctx, _parentState); + _localctx._left = _prevctx; this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 140; + this.state = 148; if (!(this.precpred(this._ctx, 2))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 2)"); } - this.state = 141; - (_localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); + this.state = 149; + _localctx._operator = this._input.LT(1); _la = this._input.LA(1); - if (!(((((_la - 44)) & ~0x1F) === 0 && ((1 << (_la - 44)) & ((1 << (esql_parser.ASTERISK - 44)) | (1 << (esql_parser.SLASH - 44)) | (1 << (esql_parser.PERCENT - 44)))) !== 0))) { - (_localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); + if (!(((((_la - 33)) & ~0x1F) === 0 && ((1 << (_la - 33)) & ((1 << (esql_parser.ASTERISK - 33)) | (1 << (esql_parser.SLASH - 33)) | (1 << (esql_parser.PERCENT - 33)))) !== 0))) { + _localctx._operator = this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { this.matchedEOF = true; @@ -734,25 +747,25 @@ export class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 142; - (_localctx as ArithmeticBinaryContext)._right = this.operatorExpression(3); + this.state = 150; + _localctx._right = this.operatorExpression(3); } break; case 2: { - _localctx = new ArithmeticBinaryContext(new OperatorExpressionContext(_parentctx, _parentState)); - (_localctx as ArithmeticBinaryContext)._left = _prevctx; + _localctx = new OperatorExpressionContext(_parentctx, _parentState); + _localctx._left = _prevctx; this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 143; + this.state = 151; if (!(this.precpred(this._ctx, 1))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 1)"); } - this.state = 144; - (_localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); + this.state = 152; + _localctx._operator = this._input.LT(1); _la = this._input.LA(1); if (!(_la === esql_parser.PLUS || _la === esql_parser.MINUS)) { - (_localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); + _localctx._operator = this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { this.matchedEOF = true; @@ -761,14 +774,14 @@ export class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 145; - (_localctx as ArithmeticBinaryContext)._right = this.operatorExpression(2); + this.state = 153; + _localctx._right = this.operatorExpression(2); } break; } } } - this.state = 150; + this.state = 158; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 11, this._ctx); } @@ -791,78 +804,74 @@ export class esql_parser extends Parser { // @RuleVersion(0) public primaryExpression(): PrimaryExpressionContext { let _localctx: PrimaryExpressionContext = new PrimaryExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 16, esql_parser.RULE_primaryExpression); + this.enterRule(_localctx, 20, esql_parser.RULE_primaryExpression); let _la: number; try { - this.state = 171; + this.state = 179; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 14, this._ctx) ) { case 1: - _localctx = new ConstantDefaultContext(_localctx); this.enterOuterAlt(_localctx, 1); { - this.state = 151; + this.state = 159; this.constant(); } break; case 2: - _localctx = new DereferenceContext(_localctx); this.enterOuterAlt(_localctx, 2); { - this.state = 152; + this.state = 160; this.qualifiedName(); } break; case 3: - _localctx = new ParenthesizedExpressionContext(_localctx); this.enterOuterAlt(_localctx, 3); { - this.state = 153; + this.state = 161; this.match(esql_parser.LP); - this.state = 154; + this.state = 162; this.booleanExpression(0); - this.state = 155; + this.state = 163; this.match(esql_parser.RP); } break; case 4: - _localctx = new FunctionExpressionContext(_localctx); this.enterOuterAlt(_localctx, 4); { - this.state = 157; + this.state = 165; this.identifier(); - this.state = 158; + this.state = 166; this.match(esql_parser.LP); - this.state = 167; + this.state = 175; this._errHandler.sync(this); _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & ((1 << esql_parser.STRING) | (1 << esql_parser.INTEGER_LITERAL) | (1 << esql_parser.DECIMAL_LITERAL) | (1 << esql_parser.FALSE) | (1 << esql_parser.LP) | (1 << esql_parser.NOT) | (1 << esql_parser.NULL))) !== 0) || ((((_la - 35)) & ~0x1F) === 0 && ((1 << (_la - 35)) & ((1 << (esql_parser.TRUE - 35)) | (1 << (esql_parser.PLUS - 35)) | (1 << (esql_parser.MINUS - 35)) | (1 << (esql_parser.ROUND_FUNCTION_MATH - 35)) | (1 << (esql_parser.AVG_FUNCTION_MATH - 35)) | (1 << (esql_parser.SUM_FUNCTION_MATH - 35)) | (1 << (esql_parser.MIN_FUNCTION_MATH - 35)) | (1 << (esql_parser.MAX_FUNCTION_MATH - 35)) | (1 << (esql_parser.UNQUOTED_IDENTIFIER - 35)) | (1 << (esql_parser.QUOTED_IDENTIFIER - 35)))) !== 0)) { + if (((((_la - 14)) & ~0x1F) === 0 && ((1 << (_la - 14)) & ((1 << (esql_parser.STRING - 14)) | (1 << (esql_parser.INTEGER_LITERAL - 14)) | (1 << (esql_parser.DECIMAL_LITERAL - 14)) | (1 << (esql_parser.LP - 14)) | (1 << (esql_parser.NOT - 14)) | (1 << (esql_parser.NULL - 14)) | (1 << (esql_parser.BOOLEAN_VALUE - 14)) | (1 << (esql_parser.PLUS - 14)) | (1 << (esql_parser.MINUS - 14)) | (1 << (esql_parser.UNARY_FUNCTION - 14)) | (1 << (esql_parser.UNQUOTED_IDENTIFIER - 14)) | (1 << (esql_parser.QUOTED_IDENTIFIER - 14)))) !== 0)) { { - this.state = 159; + this.state = 167; this.booleanExpression(0); - this.state = 164; + this.state = 172; this._errHandler.sync(this); _la = this._input.LA(1); while (_la === esql_parser.COMMA) { { { - this.state = 160; + this.state = 168; this.match(esql_parser.COMMA); - this.state = 161; + this.state = 169; this.booleanExpression(0); } } - this.state = 166; + this.state = 174; this._errHandler.sync(this); _la = this._input.LA(1); } } } - this.state = 169; + this.state = 177; this.match(esql_parser.RP); } break; @@ -885,13 +894,13 @@ export class esql_parser extends Parser { // @RuleVersion(0) public rowCommand(): RowCommandContext { let _localctx: RowCommandContext = new RowCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 18, esql_parser.RULE_rowCommand); + this.enterRule(_localctx, 22, esql_parser.RULE_rowCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 173; + this.state = 181; this.match(esql_parser.ROW); - this.state = 174; + this.state = 182; this.fields(); } } @@ -912,28 +921,28 @@ export class esql_parser extends Parser { // @RuleVersion(0) public fields(): FieldsContext { let _localctx: FieldsContext = new FieldsContext(this._ctx, this.state); - this.enterRule(_localctx, 20, esql_parser.RULE_fields); + this.enterRule(_localctx, 24, esql_parser.RULE_fields); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 176; + this.state = 184; this.field(); - this.state = 181; + this.state = 189; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 15, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 177; + this.state = 185; this.match(esql_parser.COMMA); - this.state = 178; + this.state = 186; this.field(); } } } - this.state = 183; + this.state = 191; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 15, this._ctx); } @@ -956,39 +965,27 @@ export class esql_parser extends Parser { // @RuleVersion(0) public field(): FieldContext { let _localctx: FieldContext = new FieldContext(this._ctx, this.state); - this.enterRule(_localctx, 22, esql_parser.RULE_field); + this.enterRule(_localctx, 26, esql_parser.RULE_field); try { - this.state = 193; + this.state = 197; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 16, this._ctx) ) { case 1: this.enterOuterAlt(_localctx, 1); { - this.state = 184; - this.qualifiedName(); - this.state = 185; - this.match(esql_parser.ASSIGN); - this.state = 186; - this.valueExpression(); + this.state = 192; + this.booleanExpression(0); } break; case 2: this.enterOuterAlt(_localctx, 2); { - this.state = 188; - this.booleanExpression(0); - } - break; - - case 3: - this.enterOuterAlt(_localctx, 3); - { - this.state = 189; - this.qualifiedName(); - this.state = 190; + this.state = 193; + this.userVariable(); + this.state = 194; this.match(esql_parser.ASSIGN); - this.state = 191; + this.state = 195; this.booleanExpression(0); } break; @@ -1009,32 +1006,57 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) + public userVariable(): UserVariableContext { + let _localctx: UserVariableContext = new UserVariableContext(this._ctx, this.state); + this.enterRule(_localctx, 28, esql_parser.RULE_userVariable); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 199; + this.identifier(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + // @RuleVersion(0) public fromCommand(): FromCommandContext { let _localctx: FromCommandContext = new FromCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 24, esql_parser.RULE_fromCommand); + this.enterRule(_localctx, 30, esql_parser.RULE_fromCommand); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 195; + this.state = 201; this.match(esql_parser.FROM); - this.state = 196; + this.state = 202; this.sourceIdentifier(); - this.state = 201; + this.state = 207; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 17, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 197; + this.state = 203; this.match(esql_parser.COMMA); - this.state = 198; + this.state = 204; this.sourceIdentifier(); } } } - this.state = 203; + this.state = 209; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 17, this._ctx); } @@ -1057,13 +1079,13 @@ export class esql_parser extends Parser { // @RuleVersion(0) public evalCommand(): EvalCommandContext { let _localctx: EvalCommandContext = new EvalCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 26, esql_parser.RULE_evalCommand); + this.enterRule(_localctx, 32, esql_parser.RULE_evalCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 204; + this.state = 210; this.match(esql_parser.EVAL); - this.state = 205; + this.state = 211; this.fields(); } } @@ -1084,22 +1106,22 @@ export class esql_parser extends Parser { // @RuleVersion(0) public statsCommand(): StatsCommandContext { let _localctx: StatsCommandContext = new StatsCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 28, esql_parser.RULE_statsCommand); + this.enterRule(_localctx, 34, esql_parser.RULE_statsCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 207; + this.state = 213; this.match(esql_parser.STATS); - this.state = 208; + this.state = 214; this.fields(); - this.state = 211; + this.state = 217; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 18, this._ctx) ) { case 1: { - this.state = 209; + this.state = 215; this.match(esql_parser.BY); - this.state = 210; + this.state = 216; this.qualifiedNames(); } break; @@ -1123,12 +1145,12 @@ export class esql_parser extends Parser { // @RuleVersion(0) public sourceIdentifier(): SourceIdentifierContext { let _localctx: SourceIdentifierContext = new SourceIdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 30, esql_parser.RULE_sourceIdentifier); + this.enterRule(_localctx, 36, esql_parser.RULE_sourceIdentifier); let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 213; + this.state = 219; _la = this._input.LA(1); if (!(_la === esql_parser.SRC_UNQUOTED_IDENTIFIER || _la === esql_parser.SRC_QUOTED_IDENTIFIER)) { this._errHandler.recoverInline(this); @@ -1159,23 +1181,23 @@ export class esql_parser extends Parser { // @RuleVersion(0) public functionExpressionArgument(): FunctionExpressionArgumentContext { let _localctx: FunctionExpressionArgumentContext = new FunctionExpressionArgumentContext(this._ctx, this.state); - this.enterRule(_localctx, 32, esql_parser.RULE_functionExpressionArgument); + this.enterRule(_localctx, 38, esql_parser.RULE_functionExpressionArgument); try { - this.state = 217; + this.state = 223; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.UNQUOTED_IDENTIFIER: case esql_parser.QUOTED_IDENTIFIER: this.enterOuterAlt(_localctx, 1); { - this.state = 215; + this.state = 221; this.qualifiedName(); } break; case esql_parser.STRING: this.enterOuterAlt(_localctx, 2); { - this.state = 216; + this.state = 222; this.string(); } break; @@ -1200,28 +1222,28 @@ export class esql_parser extends Parser { // @RuleVersion(0) public qualifiedName(): QualifiedNameContext { let _localctx: QualifiedNameContext = new QualifiedNameContext(this._ctx, this.state); - this.enterRule(_localctx, 34, esql_parser.RULE_qualifiedName); + this.enterRule(_localctx, 40, esql_parser.RULE_qualifiedName); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 219; + this.state = 225; this.identifier(); - this.state = 224; + this.state = 230; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 20, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 220; + this.state = 226; this.match(esql_parser.DOT); - this.state = 221; + this.state = 227; this.identifier(); } } } - this.state = 226; + this.state = 232; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 20, this._ctx); } @@ -1244,28 +1266,28 @@ export class esql_parser extends Parser { // @RuleVersion(0) public qualifiedNames(): QualifiedNamesContext { let _localctx: QualifiedNamesContext = new QualifiedNamesContext(this._ctx, this.state); - this.enterRule(_localctx, 36, esql_parser.RULE_qualifiedNames); + this.enterRule(_localctx, 42, esql_parser.RULE_qualifiedNames); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 227; + this.state = 233; this.qualifiedName(); - this.state = 232; + this.state = 238; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 21, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 228; + this.state = 234; this.match(esql_parser.COMMA); - this.state = 229; + this.state = 235; this.qualifiedName(); } } } - this.state = 234; + this.state = 240; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 21, this._ctx); } @@ -1288,12 +1310,12 @@ export class esql_parser extends Parser { // @RuleVersion(0) public identifier(): IdentifierContext { let _localctx: IdentifierContext = new IdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 38, esql_parser.RULE_identifier); + this.enterRule(_localctx, 44, esql_parser.RULE_identifier); let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 235; + this.state = 241; _la = this._input.LA(1); if (!(_la === esql_parser.UNQUOTED_IDENTIFIER || _la === esql_parser.QUOTED_IDENTIFIER)) { this._errHandler.recoverInline(this); @@ -1324,23 +1346,12 @@ export class esql_parser extends Parser { // @RuleVersion(0) public functionIdentifier(): FunctionIdentifierContext { let _localctx: FunctionIdentifierContext = new FunctionIdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 40, esql_parser.RULE_functionIdentifier); - let _la: number; + this.enterRule(_localctx, 46, esql_parser.RULE_functionIdentifier); try { this.enterOuterAlt(_localctx, 1); { - this.state = 237; - _la = this._input.LA(1); - if (!(((((_la - 47)) & ~0x1F) === 0 && ((1 << (_la - 47)) & ((1 << (esql_parser.ROUND_FUNCTION_MATH - 47)) | (1 << (esql_parser.AVG_FUNCTION_MATH - 47)) | (1 << (esql_parser.SUM_FUNCTION_MATH - 47)) | (1 << (esql_parser.MIN_FUNCTION_MATH - 47)) | (1 << (esql_parser.MAX_FUNCTION_MATH - 47)))) !== 0))) { - this._errHandler.recoverInline(this); - } else { - if (this._input.LA(1) === Token.EOF) { - this.matchedEOF = true; - } - - this._errHandler.reportMatch(this); - this.consume(); - } + this.state = 243; + this.match(esql_parser.UNARY_FUNCTION); } } catch (re) { @@ -1360,16 +1371,16 @@ export class esql_parser extends Parser { // @RuleVersion(0) public constant(): ConstantContext { let _localctx: ConstantContext = new ConstantContext(this._ctx, this.state); - this.enterRule(_localctx, 42, esql_parser.RULE_constant); + this.enterRule(_localctx, 48, esql_parser.RULE_constant); try { - this.state = 243; + this.state = 249; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.NULL: _localctx = new NullLiteralContext(_localctx); this.enterOuterAlt(_localctx, 1); { - this.state = 239; + this.state = 245; this.match(esql_parser.NULL); } break; @@ -1378,16 +1389,15 @@ export class esql_parser extends Parser { _localctx = new NumericLiteralContext(_localctx); this.enterOuterAlt(_localctx, 2); { - this.state = 240; + this.state = 246; this.number(); } break; - case esql_parser.FALSE: - case esql_parser.TRUE: + case esql_parser.BOOLEAN_VALUE: _localctx = new BooleanLiteralContext(_localctx); this.enterOuterAlt(_localctx, 3); { - this.state = 241; + this.state = 247; this.booleanValue(); } break; @@ -1395,7 +1405,7 @@ export class esql_parser extends Parser { _localctx = new StringLiteralContext(_localctx); this.enterOuterAlt(_localctx, 4); { - this.state = 242; + this.state = 248; this.string(); } break; @@ -1420,13 +1430,13 @@ export class esql_parser extends Parser { // @RuleVersion(0) public limitCommand(): LimitCommandContext { let _localctx: LimitCommandContext = new LimitCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 44, esql_parser.RULE_limitCommand); + this.enterRule(_localctx, 50, esql_parser.RULE_limitCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 245; + this.state = 251; this.match(esql_parser.LIMIT); - this.state = 246; + this.state = 252; this.match(esql_parser.INTEGER_LITERAL); } } @@ -1447,30 +1457,30 @@ export class esql_parser extends Parser { // @RuleVersion(0) public sortCommand(): SortCommandContext { let _localctx: SortCommandContext = new SortCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 46, esql_parser.RULE_sortCommand); + this.enterRule(_localctx, 52, esql_parser.RULE_sortCommand); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 248; + this.state = 254; this.match(esql_parser.SORT); - this.state = 249; + this.state = 255; this.orderExpression(); - this.state = 254; + this.state = 260; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 23, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 250; + this.state = 256; this.match(esql_parser.COMMA); - this.state = 251; + this.state = 257; this.orderExpression(); } } } - this.state = 256; + this.state = 262; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 23, this._ctx); } @@ -1493,53 +1503,32 @@ export class esql_parser extends Parser { // @RuleVersion(0) public orderExpression(): OrderExpressionContext { let _localctx: OrderExpressionContext = new OrderExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 48, esql_parser.RULE_orderExpression); - let _la: number; + this.enterRule(_localctx, 54, esql_parser.RULE_orderExpression); try { this.enterOuterAlt(_localctx, 1); { - this.state = 257; + this.state = 263; this.booleanExpression(0); - this.state = 259; + this.state = 265; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 24, this._ctx) ) { case 1: { - this.state = 258; - _localctx._ordering = this._input.LT(1); - _la = this._input.LA(1); - if (!(_la === esql_parser.ASC || _la === esql_parser.DESC)) { - _localctx._ordering = this._errHandler.recoverInline(this); - } else { - if (this._input.LA(1) === Token.EOF) { - this.matchedEOF = true; - } - - this._errHandler.reportMatch(this); - this.consume(); - } + this.state = 264; + this.match(esql_parser.ORDERING); } break; } - this.state = 263; + this.state = 269; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 25, this._ctx) ) { case 1: { - this.state = 261; - this.match(esql_parser.NULLS); - this.state = 262; - _localctx._nullOrdering = this._input.LT(1); - _la = this._input.LA(1); - if (!(_la === esql_parser.FIRST || _la === esql_parser.LAST)) { - _localctx._nullOrdering = this._errHandler.recoverInline(this); - } else { - if (this._input.LA(1) === Token.EOF) { - this.matchedEOF = true; - } - - this._errHandler.reportMatch(this); - this.consume(); + this.state = 267; + this.match(esql_parser.NULLS_ORDERING); + { + this.state = 268; + this.match(esql_parser.NULLS_ORDERING_DIRECTION); } } break; @@ -1563,30 +1552,30 @@ export class esql_parser extends Parser { // @RuleVersion(0) public projectCommand(): ProjectCommandContext { let _localctx: ProjectCommandContext = new ProjectCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 50, esql_parser.RULE_projectCommand); + this.enterRule(_localctx, 56, esql_parser.RULE_projectCommand); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 265; + this.state = 271; this.match(esql_parser.PROJECT); - this.state = 266; + this.state = 272; this.projectClause(); - this.state = 271; + this.state = 277; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 26, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 267; + this.state = 273; this.match(esql_parser.COMMA); - this.state = 268; + this.state = 274; this.projectClause(); } } } - this.state = 273; + this.state = 279; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 26, this._ctx); } @@ -1609,15 +1598,15 @@ export class esql_parser extends Parser { // @RuleVersion(0) public projectClause(): ProjectClauseContext { let _localctx: ProjectClauseContext = new ProjectClauseContext(this._ctx, this.state); - this.enterRule(_localctx, 52, esql_parser.RULE_projectClause); + this.enterRule(_localctx, 58, esql_parser.RULE_projectClause); try { - this.state = 279; + this.state = 285; this._errHandler.sync(this); switch ( this.interpreter.adaptivePredict(this._input, 27, this._ctx) ) { case 1: this.enterOuterAlt(_localctx, 1); { - this.state = 274; + this.state = 280; this.sourceIdentifier(); } break; @@ -1625,11 +1614,11 @@ export class esql_parser extends Parser { case 2: this.enterOuterAlt(_localctx, 2); { - this.state = 275; + this.state = 281; _localctx._newName = this.sourceIdentifier(); - this.state = 276; + this.state = 282; this.match(esql_parser.ASSIGN); - this.state = 277; + this.state = 283; _localctx._oldName = this.sourceIdentifier(); } break; @@ -1652,23 +1641,12 @@ export class esql_parser extends Parser { // @RuleVersion(0) public booleanValue(): BooleanValueContext { let _localctx: BooleanValueContext = new BooleanValueContext(this._ctx, this.state); - this.enterRule(_localctx, 54, esql_parser.RULE_booleanValue); - let _la: number; + this.enterRule(_localctx, 60, esql_parser.RULE_booleanValue); try { this.enterOuterAlt(_localctx, 1); { - this.state = 281; - _la = this._input.LA(1); - if (!(_la === esql_parser.FALSE || _la === esql_parser.TRUE)) { - this._errHandler.recoverInline(this); - } else { - if (this._input.LA(1) === Token.EOF) { - this.matchedEOF = true; - } - - this._errHandler.reportMatch(this); - this.consume(); - } + this.state = 287; + this.match(esql_parser.BOOLEAN_VALUE); } } catch (re) { @@ -1688,16 +1666,16 @@ export class esql_parser extends Parser { // @RuleVersion(0) public number(): NumberContext { let _localctx: NumberContext = new NumberContext(this._ctx, this.state); - this.enterRule(_localctx, 56, esql_parser.RULE_number); + this.enterRule(_localctx, 62, esql_parser.RULE_number); try { - this.state = 285; + this.state = 291; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.DECIMAL_LITERAL: _localctx = new DecimalLiteralContext(_localctx); this.enterOuterAlt(_localctx, 1); { - this.state = 283; + this.state = 289; this.match(esql_parser.DECIMAL_LITERAL); } break; @@ -1705,7 +1683,7 @@ export class esql_parser extends Parser { _localctx = new IntegerLiteralContext(_localctx); this.enterOuterAlt(_localctx, 2); { - this.state = 284; + this.state = 290; this.match(esql_parser.INTEGER_LITERAL); } break; @@ -1730,11 +1708,11 @@ export class esql_parser extends Parser { // @RuleVersion(0) public string(): StringContext { let _localctx: StringContext = new StringContext(this._ctx, this.state); - this.enterRule(_localctx, 58, esql_parser.RULE_string); + this.enterRule(_localctx, 64, esql_parser.RULE_string); try { this.enterOuterAlt(_localctx, 1); { - this.state = 287; + this.state = 293; this.match(esql_parser.STRING); } } @@ -1755,23 +1733,12 @@ export class esql_parser extends Parser { // @RuleVersion(0) public comparisonOperator(): ComparisonOperatorContext { let _localctx: ComparisonOperatorContext = new ComparisonOperatorContext(this._ctx, this.state); - this.enterRule(_localctx, 60, esql_parser.RULE_comparisonOperator); - let _la: number; + this.enterRule(_localctx, 66, esql_parser.RULE_comparisonOperator); try { this.enterOuterAlt(_localctx, 1); { - this.state = 289; - _la = this._input.LA(1); - if (!(((((_la - 36)) & ~0x1F) === 0 && ((1 << (_la - 36)) & ((1 << (esql_parser.EQ - 36)) | (1 << (esql_parser.NEQ - 36)) | (1 << (esql_parser.LT - 36)) | (1 << (esql_parser.LTE - 36)) | (1 << (esql_parser.GT - 36)) | (1 << (esql_parser.GTE - 36)))) !== 0))) { - this._errHandler.recoverInline(this); - } else { - if (this._input.LA(1) === Token.EOF) { - this.matchedEOF = true; - } - - this._errHandler.reportMatch(this); - this.consume(); - } + this.state = 295; + this.match(esql_parser.COMPARISON_OPERATOR); } } catch (re) { @@ -1791,13 +1758,13 @@ export class esql_parser extends Parser { // @RuleVersion(0) public explainCommand(): ExplainCommandContext { let _localctx: ExplainCommandContext = new ExplainCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 62, esql_parser.RULE_explainCommand); + this.enterRule(_localctx, 68, esql_parser.RULE_explainCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 291; + this.state = 297; this.match(esql_parser.EXPLAIN); - this.state = 292; + this.state = 298; this.subqueryExpression(); } } @@ -1818,15 +1785,15 @@ export class esql_parser extends Parser { // @RuleVersion(0) public subqueryExpression(): SubqueryExpressionContext { let _localctx: SubqueryExpressionContext = new SubqueryExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 64, esql_parser.RULE_subqueryExpression); + this.enterRule(_localctx, 70, esql_parser.RULE_subqueryExpression); try { this.enterOuterAlt(_localctx, 1); { - this.state = 294; + this.state = 300; this.match(esql_parser.OPENING_BRACKET); - this.state = 295; + this.state = 301; this.query(0); - this.state = 296; + this.state = 302; this.match(esql_parser.CLOSING_BRACKET); } } @@ -1853,7 +1820,7 @@ export class esql_parser extends Parser { case 5: return this.booleanExpression_sempred(_localctx as BooleanExpressionContext, predIndex); - case 7: + case 9: return this.operatorExpression_sempred(_localctx as OperatorExpressionContext, predIndex); } return true; @@ -1887,140 +1854,141 @@ export class esql_parser extends Parser { } public static readonly _serializedATN: string = - "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x03@\u012D\x04\x02" + + "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x033\u0133\x04\x02" + "\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04\x06\t\x06\x04\x07" + "\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f\t\f\x04\r\t\r\x04" + "\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11\x04\x12\t\x12\x04" + "\x13\t\x13\x04\x14\t\x14\x04\x15\t\x15\x04\x16\t\x16\x04\x17\t\x17\x04" + "\x18\t\x18\x04\x19\t\x19\x04\x1A\t\x1A\x04\x1B\t\x1B\x04\x1C\t\x1C\x04" + - "\x1D\t\x1D\x04\x1E\t\x1E\x04\x1F\t\x1F\x04 \t \x04!\t!\x04\"\t\"\x03\x02" + - "\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x07\x03" + - "N\n\x03\f\x03\x0E\x03Q\v\x03\x03\x04\x03\x04\x03\x04\x05\x04V\n\x04\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x05\x05^\n\x05\x03\x06\x03" + - "\x06\x03\x06\x03\x07\x03\x07\x03\x07\x03\x07\x05\x07g\n\x07\x03\x07\x03" + - "\x07\x03\x07\x03\x07\x03\x07\x03\x07\x07\x07o\n\x07\f\x07\x0E\x07r\v\x07" + - "\x03\b\x03\b\x03\b\x03\b\x03\b\x07\by\n\b\f\b\x0E\b|\v\b\x05\b~\n\b\x03" + - "\b\x03\b\x03\b\x03\b\x03\b\x03\b\x03\b\x05\b\x87\n\b\x03\t\x03\t\x03\t" + - "\x03\t\x05\t\x8D\n\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03\t\x07\t\x95\n\t" + - "\f\t\x0E\t\x98\v\t\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03" + - "\n\x03\n\x03\n\x07\n\xA5\n\n\f\n\x0E\n\xA8\v\n\x05\n\xAA\n\n\x03\n\x03" + - "\n\x05\n\xAE\n\n\x03\v\x03\v\x03\v\x03\f\x03\f\x03\f\x07\f\xB6\n\f\f\f" + - "\x0E\f\xB9\v\f\x03\r\x03\r\x03\r\x03\r\x03\r\x03\r\x03\r\x03\r\x03\r\x05" + - "\r\xC4\n\r\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x07\x0E\xCA\n\x0E\f\x0E\x0E" + - "\x0E\xCD\v\x0E\x03\x0F\x03\x0F\x03\x0F\x03\x10\x03\x10\x03\x10\x03\x10" + - "\x05\x10\xD6\n\x10\x03\x11\x03\x11\x03\x12\x03\x12\x05\x12\xDC\n\x12\x03" + - "\x13\x03\x13\x03\x13\x07\x13\xE1\n\x13\f\x13\x0E\x13\xE4\v\x13\x03\x14" + - "\x03\x14\x03\x14\x07\x14\xE9\n\x14\f\x14\x0E\x14\xEC\v\x14\x03\x15\x03" + - "\x15\x03\x16\x03\x16\x03\x17\x03\x17\x03\x17\x03\x17\x05\x17\xF6\n\x17" + - "\x03\x18\x03\x18\x03\x18\x03\x19\x03\x19\x03\x19\x03\x19\x07\x19\xFF\n" + - "\x19\f\x19\x0E\x19\u0102\v\x19\x03\x1A\x03\x1A\x05\x1A\u0106\n\x1A\x03" + - "\x1A\x03\x1A\x05\x1A\u010A\n\x1A\x03\x1B\x03\x1B\x03\x1B\x03\x1B\x07\x1B" + - "\u0110\n\x1B\f\x1B\x0E\x1B\u0113\v\x1B\x03\x1C\x03\x1C\x03\x1C\x03\x1C" + - "\x03\x1C\x05\x1C\u011A\n\x1C\x03\x1D\x03\x1D\x03\x1E\x03\x1E\x05\x1E\u0120" + - "\n\x1E\x03\x1F\x03\x1F\x03 \x03 \x03!\x03!\x03!\x03\"\x03\"\x03\"\x03" + - "\"\x03\"\x02\x02\x05\x04\f\x10#\x02\x02\x04\x02\x06\x02\b\x02\n\x02\f" + - "\x02\x0E\x02\x10\x02\x12\x02\x14\x02\x16\x02\x18\x02\x1A\x02\x1C\x02\x1E" + - "\x02 \x02\"\x02$\x02&\x02(\x02*\x02,\x02.\x020\x022\x024\x026\x028\x02" + - ":\x02<\x02>\x02@\x02B\x02\x02\v\x03\x02,-\x03\x02.0\x03\x02;<\x03\x02" + - "67\x03\x0215\x04\x02\x15\x15\x18\x18\x03\x02\x1B\x1C\x04\x02\x1A\x1A%" + - "%\x03\x02&+\x02\u0133\x02D\x03\x02\x02\x02\x04G\x03\x02\x02\x02\x06U\x03" + - "\x02\x02\x02\b]\x03\x02\x02\x02\n_\x03\x02\x02\x02\ff\x03\x02\x02\x02" + - "\x0E\x86\x03\x02\x02\x02\x10\x8C\x03\x02\x02\x02\x12\xAD\x03\x02\x02\x02" + - "\x14\xAF\x03\x02\x02\x02\x16\xB2\x03\x02\x02\x02\x18\xC3\x03\x02\x02\x02" + - "\x1A\xC5\x03\x02\x02\x02\x1C\xCE\x03\x02\x02\x02\x1E\xD1\x03\x02\x02\x02" + - " \xD7\x03\x02\x02\x02\"\xDB\x03\x02\x02\x02$\xDD\x03\x02\x02\x02&\xE5" + - "\x03\x02\x02\x02(\xED\x03\x02\x02\x02*\xEF\x03\x02\x02\x02,\xF5\x03\x02" + - "\x02\x02.\xF7\x03\x02\x02\x020\xFA\x03\x02\x02\x022\u0103\x03\x02\x02" + - "\x024\u010B\x03\x02\x02\x026\u0119\x03\x02\x02\x028\u011B\x03\x02\x02" + - "\x02:\u011F\x03\x02\x02\x02<\u0121\x03\x02\x02\x02>\u0123\x03\x02\x02" + - "\x02@\u0125\x03\x02\x02\x02B\u0128\x03\x02\x02\x02DE\x05\x04\x03\x02E" + - "F\x07\x02\x02\x03F\x03\x03\x02\x02\x02GH\b\x03\x01\x02HI\x05\x06\x04\x02" + - "IO\x03\x02\x02\x02JK\f\x03\x02\x02KL\x07\x0F\x02\x02LN\x05\b\x05\x02M" + - "J\x03\x02\x02\x02NQ\x03\x02\x02\x02OM\x03\x02\x02\x02OP\x03\x02\x02\x02" + - "P\x05\x03\x02\x02\x02QO\x03\x02\x02\x02RV\x05@!\x02SV\x05\x1A\x0E\x02" + - "TV\x05\x14\v\x02UR\x03\x02\x02\x02US\x03\x02\x02\x02UT\x03\x02\x02\x02" + - "V\x07\x03\x02\x02\x02W^\x05\x1C\x0F\x02X^\x05.\x18\x02Y^\x054\x1B\x02" + - "Z^\x050\x19\x02[^\x05\x1E\x10\x02\\^\x05\n\x06\x02]W\x03\x02\x02\x02]" + - "X\x03\x02\x02\x02]Y\x03\x02\x02\x02]Z\x03\x02\x02\x02][\x03\x02\x02\x02" + - "]\\\x03\x02\x02\x02^\t\x03\x02\x02\x02_`\x07\b\x02\x02`a\x05\f\x07\x02" + - "a\v\x03\x02\x02\x02bc\b\x07\x01\x02cd\x07 \x02\x02dg\x05\f\x07\x06eg\x05" + - "\x0E\b\x02fb\x03\x02\x02\x02fe\x03\x02\x02\x02gp\x03\x02\x02\x02hi\f\x04" + - "\x02\x02ij\x07\x14\x02\x02jo\x05\f\x07\x05kl\f\x03\x02\x02lm\x07#\x02" + - "\x02mo\x05\f\x07\x04nh\x03\x02\x02\x02nk\x03\x02\x02\x02or\x03\x02\x02" + - "\x02pn\x03\x02\x02\x02pq\x03\x02\x02\x02q\r\x03\x02\x02\x02rp\x03\x02" + - "\x02\x02st\x05*\x16\x02t}\x07\x1D\x02\x02uz\x05\"\x12\x02vw\x07\x17\x02" + - "\x02wy\x05\"\x12\x02xv\x03\x02\x02\x02y|\x03\x02\x02\x02zx\x03\x02\x02" + - "\x02z{\x03\x02\x02\x02{~\x03\x02\x02\x02|z\x03\x02\x02\x02}u\x03\x02\x02" + - "\x02}~\x03\x02\x02\x02~\x7F\x03\x02\x02\x02\x7F\x80\x07$\x02\x02\x80\x87" + - "\x03\x02\x02\x02\x81\x87\x05\x10\t\x02\x82\x83\x05\x10\t\x02\x83\x84\x05" + - "> \x02\x84\x85\x05\x10\t\x02\x85\x87\x03\x02\x02\x02\x86s\x03\x02\x02" + - "\x02\x86\x81\x03\x02\x02\x02\x86\x82\x03\x02\x02\x02\x87\x0F\x03\x02\x02" + - "\x02\x88\x89\b\t\x01\x02\x89\x8D\x05\x12\n\x02\x8A\x8B\t\x02\x02\x02\x8B" + - "\x8D\x05\x10\t\x05\x8C\x88\x03\x02\x02\x02\x8C\x8A\x03\x02\x02\x02\x8D" + - "\x96\x03\x02\x02\x02\x8E\x8F\f\x04\x02\x02\x8F\x90\t\x03\x02\x02\x90\x95" + - "\x05\x10\t\x05\x91\x92\f\x03\x02\x02\x92\x93\t\x02\x02\x02\x93\x95\x05" + - "\x10\t\x04\x94\x8E\x03\x02\x02\x02\x94\x91\x03\x02\x02\x02\x95\x98\x03" + - "\x02\x02\x02\x96\x94\x03\x02\x02\x02\x96\x97\x03\x02\x02\x02\x97\x11\x03" + - "\x02\x02\x02\x98\x96\x03\x02\x02\x02\x99\xAE\x05,\x17\x02\x9A\xAE\x05" + - "$\x13\x02\x9B\x9C\x07\x1D\x02\x02\x9C\x9D\x05\f\x07\x02\x9D\x9E\x07$\x02" + - "\x02\x9E\xAE\x03\x02\x02\x02\x9F\xA0\x05(\x15\x02\xA0\xA9\x07\x1D\x02" + - "\x02\xA1\xA6\x05\f\x07\x02\xA2\xA3\x07\x17\x02\x02\xA3\xA5\x05\f\x07\x02" + - "\xA4\xA2\x03\x02\x02\x02\xA5\xA8\x03\x02\x02\x02\xA6\xA4\x03\x02\x02\x02" + - "\xA6\xA7\x03\x02\x02\x02\xA7\xAA\x03\x02\x02\x02\xA8\xA6\x03\x02\x02\x02" + - "\xA9\xA1\x03\x02\x02\x02\xA9\xAA\x03\x02\x02\x02\xAA\xAB\x03\x02\x02\x02" + - "\xAB\xAC\x07$\x02\x02\xAC\xAE\x03\x02\x02\x02\xAD\x99\x03\x02\x02\x02" + - "\xAD\x9A\x03\x02\x02\x02\xAD\x9B\x03\x02\x02\x02\xAD\x9F\x03\x02\x02\x02" + - "\xAE\x13\x03\x02\x02\x02\xAF\xB0\x07\x06\x02\x02\xB0\xB1\x05\x16\f\x02" + - "\xB1\x15\x03\x02\x02\x02\xB2\xB7\x05\x18\r\x02\xB3\xB4\x07\x17\x02\x02" + - "\xB4\xB6\x05\x18\r\x02\xB5\xB3\x03\x02\x02\x02\xB6\xB9\x03\x02\x02\x02" + - "\xB7\xB5\x03\x02\x02\x02\xB7\xB8\x03\x02\x02\x02\xB8\x17\x03\x02\x02\x02" + - "\xB9\xB7\x03\x02\x02\x02\xBA\xBB\x05$\x13\x02\xBB\xBC\x07\x16\x02\x02" + - "\xBC\xBD\x05\x0E\b\x02\xBD\xC4\x03\x02\x02\x02\xBE\xC4\x05\f\x07\x02\xBF" + - "\xC0\x05$\x13\x02\xC0\xC1\x07\x16\x02\x02\xC1\xC2\x05\f\x07\x02\xC2\xC4" + - "\x03\x02\x02\x02\xC3\xBA\x03\x02\x02\x02\xC3\xBE\x03\x02\x02\x02\xC3\xBF" + - "\x03\x02\x02\x02\xC4\x19\x03\x02\x02\x02\xC5\xC6\x07\x05\x02\x02\xC6\xCB" + - "\x05 \x11\x02\xC7\xC8\x07\x17\x02\x02\xC8\xCA\x05 \x11\x02\xC9\xC7\x03" + - "\x02\x02\x02\xCA\xCD\x03\x02\x02\x02\xCB\xC9\x03\x02\x02\x02\xCB\xCC\x03" + - "\x02\x02\x02\xCC\x1B\x03\x02\x02\x02\xCD\xCB\x03\x02\x02\x02\xCE\xCF\x07" + - "\x03\x02\x02\xCF\xD0\x05\x16\f\x02\xD0\x1D\x03\x02\x02\x02\xD1\xD2\x07" + - "\x07\x02\x02\xD2\xD5\x05\x16\f\x02\xD3\xD4\x07\x13\x02\x02\xD4\xD6\x05" + - "&\x14\x02\xD5\xD3\x03\x02\x02\x02\xD5\xD6\x03\x02\x02\x02\xD6\x1F\x03" + - "\x02\x02\x02\xD7\xD8\t\x04\x02\x02\xD8!\x03\x02\x02\x02\xD9\xDC\x05$\x13" + - "\x02\xDA\xDC\x05<\x1F\x02\xDB\xD9\x03\x02\x02\x02\xDB\xDA\x03\x02\x02" + - "\x02\xDC#\x03\x02\x02\x02\xDD\xE2\x05(\x15\x02\xDE\xDF\x07\x19\x02\x02" + - "\xDF\xE1\x05(\x15\x02\xE0\xDE\x03\x02\x02\x02\xE1\xE4\x03\x02\x02\x02" + - "\xE2\xE0\x03\x02\x02\x02\xE2\xE3\x03\x02\x02\x02\xE3%\x03\x02\x02\x02" + - "\xE4\xE2\x03\x02\x02\x02\xE5\xEA\x05$\x13\x02\xE6\xE7\x07\x17\x02\x02" + - "\xE7\xE9\x05$\x13\x02\xE8\xE6\x03\x02\x02\x02\xE9\xEC\x03\x02\x02\x02" + - "\xEA\xE8\x03\x02\x02\x02\xEA\xEB\x03\x02\x02\x02\xEB\'\x03\x02\x02\x02" + - "\xEC\xEA\x03\x02\x02\x02\xED\xEE\t\x05\x02\x02\xEE)\x03\x02\x02\x02\xEF" + - "\xF0\t\x06\x02\x02\xF0+\x03\x02\x02\x02\xF1\xF6\x07!\x02\x02\xF2\xF6\x05" + - ":\x1E\x02\xF3\xF6\x058\x1D\x02\xF4\xF6\x05<\x1F\x02\xF5\xF1\x03\x02\x02" + - "\x02\xF5\xF2\x03\x02\x02\x02\xF5\xF3\x03\x02\x02\x02\xF5\xF4\x03\x02\x02" + - "\x02\xF6-\x03\x02\x02\x02\xF7\xF8\x07\n\x02\x02\xF8\xF9\x07\x11\x02\x02" + - "\xF9/\x03\x02\x02\x02\xFA\xFB\x07\t\x02\x02\xFB\u0100\x052\x1A\x02\xFC" + - "\xFD\x07\x17\x02\x02\xFD\xFF\x052\x1A\x02\xFE\xFC\x03\x02\x02\x02\xFF" + - "\u0102\x03\x02\x02\x02\u0100\xFE\x03\x02\x02\x02\u0100\u0101\x03\x02\x02" + - "\x02\u01011\x03\x02\x02\x02\u0102\u0100\x03\x02\x02\x02\u0103\u0105\x05" + - "\f\x07\x02\u0104\u0106\t\x07\x02\x02\u0105\u0104\x03\x02\x02\x02\u0105" + - "\u0106\x03\x02\x02\x02\u0106\u0109\x03\x02\x02\x02\u0107\u0108\x07\"\x02" + - "\x02\u0108\u010A\t\b\x02\x02\u0109\u0107\x03\x02\x02\x02\u0109\u010A\x03" + - "\x02\x02\x02\u010A3\x03\x02\x02\x02\u010B\u010C\x07\v\x02\x02\u010C\u0111" + - "\x056\x1C\x02\u010D\u010E\x07\x17\x02\x02\u010E\u0110\x056\x1C\x02\u010F" + - "\u010D\x03\x02\x02\x02\u0110\u0113\x03\x02\x02\x02\u0111\u010F\x03\x02" + - "\x02\x02\u0111\u0112\x03\x02\x02\x02\u01125\x03\x02\x02\x02\u0113\u0111" + - "\x03\x02\x02\x02\u0114\u011A\x05 \x11\x02\u0115\u0116\x05 \x11\x02\u0116" + - "\u0117\x07\x16\x02\x02\u0117\u0118\x05 \x11\x02\u0118\u011A\x03\x02\x02" + - "\x02\u0119\u0114\x03\x02\x02\x02\u0119\u0115\x03\x02\x02\x02\u011A7\x03" + - "\x02\x02\x02\u011B\u011C\t\t\x02\x02\u011C9\x03\x02\x02\x02\u011D\u0120" + - "\x07\x12\x02\x02\u011E\u0120\x07\x11\x02\x02\u011F\u011D\x03\x02\x02\x02" + - "\u011F\u011E\x03\x02\x02\x02\u0120;\x03\x02\x02\x02\u0121\u0122\x07\x10" + - "\x02\x02\u0122=\x03\x02\x02\x02\u0123\u0124\t\n\x02\x02\u0124?\x03\x02" + - "\x02\x02\u0125\u0126\x07\x04\x02\x02\u0126\u0127\x05B\"\x02\u0127A\x03" + - "\x02\x02\x02\u0128\u0129\x07\x1E\x02\x02\u0129\u012A\x05\x04\x03\x02\u012A" + - "\u012B\x07\x1F\x02\x02\u012BC\x03\x02\x02\x02\x1FOU]fnpz}\x86\x8C\x94" + - "\x96\xA6\xA9\xAD\xB7\xC3\xCB\xD5\xDB\xE2\xEA\xF5\u0100\u0105\u0109\u0111" + - "\u0119\u011F"; + "\x1D\t\x1D\x04\x1E\t\x1E\x04\x1F\t\x1F\x04 \t \x04!\t!\x04\"\t\"\x04#" + + "\t#\x04$\t$\x04%\t%\x03\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03" + + "\x03\x03\x03\x03\x03\x07\x03T\n\x03\f\x03\x0E\x03W\v\x03\x03\x04\x03\x04" + + "\x03\x04\x05\x04\\\n\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05" + + "\x05\x05d\n\x05\x03\x06\x03\x06\x03\x06\x03\x07\x03\x07\x03\x07\x03\x07" + + "\x05\x07m\n\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x07\x07" + + "u\n\x07\f\x07\x0E\x07x\v\x07\x03\b\x03\b\x05\b|\n\b\x03\t\x03\t\x03\t" + + "\x03\t\x03\n\x03\n\x03\n\x03\n\x03\n\x07\n\x87\n\n\f\n\x0E\n\x8A\v\n\x05" + + "\n\x8C\n\n\x03\n\x03\n\x03\v\x03\v\x03\v\x03\v\x03\v\x05\v\x95\n\v\x03" + + "\v\x03\v\x03\v\x03\v\x03\v\x03\v\x07\v\x9D\n\v\f\v\x0E\v\xA0\v\v\x03\f" + + "\x03\f\x03\f\x03\f\x03\f\x03\f\x03\f\x03\f\x03\f\x03\f\x03\f\x07\f\xAD" + + "\n\f\f\f\x0E\f\xB0\v\f\x05\f\xB2\n\f\x03\f\x03\f\x05\f\xB6\n\f\x03\r\x03" + + "\r\x03\r\x03\x0E\x03\x0E\x03\x0E\x07\x0E\xBE\n\x0E\f\x0E\x0E\x0E\xC1\v" + + "\x0E\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x05\x0F\xC8\n\x0F\x03\x10" + + "\x03\x10\x03\x11\x03\x11\x03\x11\x03\x11\x07\x11\xD0\n\x11\f\x11\x0E\x11" + + "\xD3\v\x11\x03\x12\x03\x12\x03\x12\x03\x13\x03\x13\x03\x13\x03\x13\x05" + + "\x13\xDC\n\x13\x03\x14\x03\x14\x03\x15\x03\x15\x05\x15\xE2\n\x15\x03\x16" + + "\x03\x16\x03\x16\x07\x16\xE7\n\x16\f\x16\x0E\x16\xEA\v\x16\x03\x17\x03" + + "\x17\x03\x17\x07\x17\xEF\n\x17\f\x17\x0E\x17\xF2\v\x17\x03\x18\x03\x18" + + "\x03\x19\x03\x19\x03\x1A\x03\x1A\x03\x1A\x03\x1A\x05\x1A\xFC\n\x1A\x03" + + "\x1B\x03\x1B\x03\x1B\x03\x1C\x03\x1C\x03\x1C\x03\x1C\x07\x1C\u0105\n\x1C" + + "\f\x1C\x0E\x1C\u0108\v\x1C\x03\x1D\x03\x1D\x05\x1D\u010C\n\x1D\x03\x1D" + + "\x03\x1D\x05\x1D\u0110\n\x1D\x03\x1E\x03\x1E\x03\x1E\x03\x1E\x07\x1E\u0116" + + "\n\x1E\f\x1E\x0E\x1E\u0119\v\x1E\x03\x1F\x03\x1F\x03\x1F\x03\x1F\x03\x1F" + + "\x05\x1F\u0120\n\x1F\x03 \x03 \x03!\x03!\x05!\u0126\n!\x03\"\x03\"\x03" + + "#\x03#\x03$\x03$\x03$\x03%\x03%\x03%\x03%\x03%\x02\x02\x05\x04\f\x14&" + + "\x02\x02\x04\x02\x06\x02\b\x02\n\x02\f\x02\x0E\x02\x10\x02\x12\x02\x14" + + "\x02\x16\x02\x18\x02\x1A\x02\x1C\x02\x1E\x02 \x02\"\x02$\x02&\x02(\x02" + + "*\x02,\x02.\x020\x022\x024\x026\x028\x02:\x02<\x02>\x02@\x02B\x02D\x02" + + "F\x02H\x02\x02\x06\x03\x02!\"\x03\x02#%\x03\x02/0\x03\x02*+\x02\u0135" + + "\x02J\x03\x02\x02\x02\x04M\x03\x02\x02\x02\x06[\x03\x02\x02\x02\bc\x03" + + "\x02\x02\x02\ne\x03\x02\x02\x02\fl\x03\x02\x02\x02\x0E{\x03\x02\x02\x02" + + "\x10}\x03\x02\x02\x02\x12\x81\x03\x02\x02\x02\x14\x94\x03\x02\x02\x02" + + "\x16\xB5\x03\x02\x02\x02\x18\xB7\x03\x02\x02\x02\x1A\xBA\x03\x02\x02\x02" + + "\x1C\xC7\x03\x02\x02\x02\x1E\xC9\x03\x02\x02\x02 \xCB\x03\x02\x02\x02" + + "\"\xD4\x03\x02\x02\x02$\xD7\x03\x02\x02\x02&\xDD\x03\x02\x02\x02(\xE1" + + "\x03\x02\x02\x02*\xE3\x03\x02\x02\x02,\xEB\x03\x02\x02\x02.\xF3\x03\x02" + + "\x02\x020\xF5\x03\x02\x02\x022\xFB\x03\x02\x02\x024\xFD\x03\x02\x02\x02" + + "6\u0100\x03\x02\x02\x028\u0109\x03\x02\x02\x02:\u0111\x03\x02\x02\x02" + + "<\u011F\x03\x02\x02\x02>\u0121\x03\x02\x02\x02@\u0125\x03\x02\x02\x02" + + "B\u0127\x03\x02\x02\x02D\u0129\x03\x02\x02\x02F\u012B\x03\x02\x02\x02" + + "H\u012E\x03\x02\x02\x02JK\x05\x04\x03\x02KL\x07\x02\x02\x03L\x03\x03\x02" + + "\x02\x02MN\b\x03\x01\x02NO\x05\x06\x04\x02OU\x03\x02\x02\x02PQ\f\x03\x02" + + "\x02QR\x07\x0F\x02\x02RT\x05\b\x05\x02SP\x03\x02\x02\x02TW\x03\x02\x02" + + "\x02US\x03\x02\x02\x02UV\x03\x02\x02\x02V\x05\x03\x02\x02\x02WU\x03\x02" + + "\x02\x02X\\\x05F$\x02Y\\\x05 \x11\x02Z\\\x05\x18\r\x02[X\x03\x02\x02\x02" + + "[Y\x03\x02\x02\x02[Z\x03\x02\x02\x02\\\x07\x03\x02\x02\x02]d\x05\"\x12" + + "\x02^d\x054\x1B\x02_d\x05:\x1E\x02`d\x056\x1C\x02ad\x05$\x13\x02bd\x05" + + "\n\x06\x02c]\x03\x02\x02\x02c^\x03\x02\x02\x02c_\x03\x02\x02\x02c`\x03" + + "\x02\x02\x02ca\x03\x02\x02\x02cb\x03\x02\x02\x02d\t\x03\x02\x02\x02ef" + + "\x07\b\x02\x02fg\x05\f\x07\x02g\v\x03\x02\x02\x02hi\b\x07\x01\x02ij\x07" + + "\x1B\x02\x02jm\x05\f\x07\x06km\x05\x0E\b\x02lh\x03\x02\x02\x02lk\x03\x02" + + "\x02\x02mv\x03\x02\x02\x02no\f\x04\x02\x02op\x07\x14\x02\x02pu\x05\f\x07" + + "\x05qr\f\x03\x02\x02rs\x07\x1D\x02\x02su\x05\f\x07\x04tn\x03\x02\x02\x02" + + "tq\x03\x02\x02\x02ux\x03\x02\x02\x02vt\x03\x02\x02\x02vw\x03\x02\x02\x02" + + "w\r\x03\x02\x02\x02xv\x03\x02\x02\x02y|\x05\x14\v\x02z|\x05\x10\t\x02" + + "{y\x03\x02\x02\x02{z\x03\x02\x02\x02|\x0F\x03\x02\x02\x02}~\x05\x14\v" + + "\x02~\x7F\x05D#\x02\x7F\x80\x05\x14\v\x02\x80\x11\x03\x02\x02\x02\x81" + + "\x82\x050\x19\x02\x82\x8B\x07\x18\x02\x02\x83\x88\x05(\x15\x02\x84\x85" + + "\x07\x16\x02\x02\x85\x87\x05(\x15\x02\x86\x84\x03\x02\x02\x02\x87\x8A" + + "\x03\x02\x02\x02\x88\x86\x03\x02\x02\x02\x88\x89\x03\x02\x02\x02\x89\x8C" + + "\x03\x02\x02\x02\x8A\x88\x03\x02\x02\x02\x8B\x83\x03\x02\x02\x02\x8B\x8C" + + "\x03\x02\x02\x02\x8C\x8D\x03\x02\x02\x02\x8D\x8E\x07\x1E\x02\x02\x8E\x13" + + "\x03\x02\x02\x02\x8F\x90\b\v\x01\x02\x90\x95\x05\x16\f\x02\x91\x95\x05" + + "\x12\n\x02\x92\x93\t\x02\x02\x02\x93\x95\x05\x14\v\x05\x94\x8F\x03\x02" + + "\x02\x02\x94\x91\x03\x02\x02\x02\x94\x92\x03\x02\x02\x02\x95\x9E\x03\x02" + + "\x02\x02\x96\x97\f\x04\x02\x02\x97\x98\t\x03\x02\x02\x98\x9D\x05\x14\v" + + "\x05\x99\x9A\f\x03\x02\x02\x9A\x9B\t\x02\x02\x02\x9B\x9D\x05\x14\v\x04" + + "\x9C\x96\x03\x02\x02\x02\x9C\x99\x03\x02\x02\x02\x9D\xA0\x03\x02\x02\x02" + + "\x9E\x9C\x03\x02\x02\x02\x9E\x9F\x03\x02\x02\x02\x9F\x15\x03\x02\x02\x02" + + "\xA0\x9E\x03\x02\x02\x02\xA1\xB6\x052\x1A\x02\xA2\xB6\x05*\x16\x02\xA3" + + "\xA4\x07\x18\x02\x02\xA4\xA5\x05\f\x07\x02\xA5\xA6\x07\x1E\x02\x02\xA6" + + "\xB6\x03\x02\x02\x02\xA7\xA8\x05.\x18\x02\xA8\xB1\x07\x18\x02\x02\xA9" + + "\xAE\x05\f\x07\x02\xAA\xAB\x07\x16\x02\x02\xAB\xAD\x05\f\x07\x02\xAC\xAA" + + "\x03\x02\x02\x02\xAD\xB0\x03\x02\x02\x02\xAE\xAC\x03\x02\x02\x02\xAE\xAF" + + "\x03\x02\x02\x02\xAF\xB2\x03\x02\x02\x02\xB0\xAE\x03\x02\x02\x02\xB1\xA9" + + "\x03\x02\x02\x02\xB1\xB2\x03\x02\x02\x02\xB2\xB3\x03\x02\x02\x02\xB3\xB4" + + "\x07\x1E\x02\x02\xB4\xB6\x03\x02\x02\x02\xB5\xA1\x03\x02\x02\x02\xB5\xA2" + + "\x03\x02\x02\x02\xB5\xA3\x03\x02\x02\x02\xB5\xA7\x03\x02\x02\x02\xB6\x17" + + "\x03\x02\x02\x02\xB7\xB8\x07\x06\x02\x02\xB8\xB9\x05\x1A\x0E\x02\xB9\x19" + + "\x03\x02\x02\x02\xBA\xBF\x05\x1C\x0F\x02\xBB\xBC\x07\x16\x02\x02\xBC\xBE" + + "\x05\x1C\x0F\x02\xBD\xBB\x03\x02\x02\x02\xBE\xC1\x03\x02\x02\x02\xBF\xBD" + + "\x03\x02\x02\x02\xBF\xC0\x03\x02\x02\x02\xC0\x1B\x03\x02\x02\x02\xC1\xBF" + + "\x03\x02\x02\x02\xC2\xC8\x05\f\x07\x02\xC3\xC4\x05\x1E\x10\x02\xC4\xC5" + + "\x07\x15\x02\x02\xC5\xC6\x05\f\x07\x02\xC6\xC8\x03\x02\x02\x02\xC7\xC2" + + "\x03\x02\x02\x02\xC7\xC3\x03\x02\x02\x02\xC8\x1D\x03\x02\x02\x02\xC9\xCA" + + "\x05.\x18\x02\xCA\x1F\x03\x02\x02\x02\xCB\xCC\x07\x05\x02\x02\xCC\xD1" + + "\x05&\x14\x02\xCD\xCE\x07\x16\x02\x02\xCE\xD0\x05&\x14\x02\xCF\xCD\x03" + + "\x02\x02\x02\xD0\xD3\x03\x02\x02\x02\xD1\xCF\x03\x02\x02\x02\xD1\xD2\x03" + + "\x02\x02\x02\xD2!\x03\x02\x02\x02\xD3\xD1\x03\x02\x02\x02\xD4\xD5\x07" + + "\x03\x02\x02\xD5\xD6\x05\x1A\x0E\x02\xD6#\x03\x02\x02\x02\xD7\xD8\x07" + + "\x07\x02\x02\xD8\xDB\x05\x1A\x0E\x02\xD9\xDA\x07\x13\x02\x02\xDA\xDC\x05" + + ",\x17\x02\xDB\xD9\x03\x02\x02\x02\xDB\xDC\x03\x02\x02\x02\xDC%\x03\x02" + + "\x02\x02\xDD\xDE\t\x04\x02\x02\xDE\'\x03\x02\x02\x02\xDF\xE2\x05*\x16" + + "\x02\xE0\xE2\x05B\"\x02\xE1\xDF\x03\x02\x02\x02\xE1\xE0\x03\x02\x02\x02" + + "\xE2)\x03\x02\x02\x02\xE3\xE8\x05.\x18\x02\xE4\xE5\x07\x17\x02\x02\xE5" + + "\xE7\x05.\x18\x02\xE6\xE4\x03\x02\x02\x02\xE7\xEA\x03\x02\x02\x02\xE8" + + "\xE6\x03\x02\x02\x02\xE8\xE9\x03\x02\x02\x02\xE9+\x03\x02\x02\x02\xEA" + + "\xE8\x03\x02\x02\x02\xEB\xF0\x05*\x16\x02\xEC\xED\x07\x16\x02\x02\xED" + + "\xEF\x05*\x16\x02\xEE\xEC\x03\x02\x02\x02\xEF\xF2\x03\x02\x02\x02\xF0" + + "\xEE\x03\x02\x02\x02\xF0\xF1\x03\x02\x02\x02\xF1-\x03\x02\x02\x02\xF2" + + "\xF0\x03\x02\x02\x02\xF3\xF4\t\x05\x02\x02\xF4/\x03\x02\x02\x02\xF5\xF6" + + "\x07)\x02\x02\xF61\x03\x02\x02\x02\xF7\xFC\x07\x1C\x02\x02\xF8\xFC\x05" + + "@!\x02\xF9\xFC\x05> \x02\xFA\xFC\x05B\"\x02\xFB\xF7\x03\x02\x02\x02\xFB" + + "\xF8\x03\x02\x02\x02\xFB\xF9\x03\x02\x02\x02\xFB\xFA\x03\x02\x02\x02\xFC" + + "3\x03\x02\x02\x02\xFD\xFE\x07\n\x02\x02\xFE\xFF\x07\x11\x02\x02\xFF5\x03" + + "\x02\x02\x02\u0100\u0101\x07\t\x02\x02\u0101\u0106\x058\x1D\x02\u0102" + + "\u0103\x07\x16\x02\x02\u0103\u0105\x058\x1D\x02\u0104\u0102\x03\x02\x02" + + "\x02\u0105\u0108\x03\x02\x02\x02\u0106\u0104\x03\x02\x02\x02\u0106\u0107" + + "\x03\x02\x02\x02\u01077\x03\x02\x02\x02\u0108\u0106\x03\x02\x02\x02\u0109" + + "\u010B\x05\f\x07\x02\u010A\u010C\x07&\x02\x02\u010B\u010A\x03\x02\x02" + + "\x02\u010B\u010C\x03\x02\x02\x02\u010C\u010F\x03\x02\x02\x02\u010D\u010E" + + "\x07\'\x02\x02\u010E\u0110\x07(\x02\x02\u010F\u010D\x03\x02\x02\x02\u010F" + + "\u0110\x03\x02\x02\x02\u01109\x03\x02\x02\x02\u0111\u0112\x07\v\x02\x02" + + "\u0112\u0117\x05<\x1F\x02\u0113\u0114\x07\x16\x02\x02\u0114\u0116\x05" + + "<\x1F\x02\u0115\u0113\x03\x02\x02\x02\u0116\u0119\x03\x02\x02\x02\u0117" + + "\u0115\x03\x02\x02\x02\u0117\u0118\x03\x02\x02\x02\u0118;\x03\x02\x02" + + "\x02\u0119\u0117\x03\x02\x02\x02\u011A\u0120\x05&\x14\x02\u011B\u011C" + + "\x05&\x14\x02\u011C\u011D\x07\x15\x02\x02\u011D\u011E\x05&\x14\x02\u011E" + + "\u0120\x03\x02\x02\x02\u011F\u011A\x03\x02\x02\x02\u011F\u011B\x03\x02" + + "\x02\x02\u0120=\x03\x02\x02\x02\u0121\u0122\x07\x1F\x02\x02\u0122?\x03" + + "\x02\x02\x02\u0123\u0126\x07\x12\x02\x02\u0124\u0126\x07\x11\x02\x02\u0125" + + "\u0123\x03\x02\x02\x02\u0125\u0124\x03\x02\x02\x02\u0126A\x03\x02\x02" + + "\x02\u0127\u0128\x07\x10\x02\x02\u0128C\x03\x02\x02\x02\u0129\u012A\x07" + + " \x02\x02\u012AE\x03\x02\x02\x02\u012B\u012C\x07\x04\x02\x02\u012C\u012D" + + "\x05H%\x02\u012DG\x03\x02\x02\x02\u012E\u012F\x07\x19\x02\x02\u012F\u0130" + + "\x05\x04\x03\x02\u0130\u0131\x07\x1A\x02\x02\u0131I\x03\x02\x02\x02\x1F" + + "U[cltv{\x88\x8B\x94\x9C\x9E\xAE\xB1\xB5\xBF\xC7\xD1\xDB\xE1\xE8\xF0\xFB" + + "\u0106\u010B\u010F\u0117\u011F\u0125"; public static __ATN: ATN; public static get _ATN(): ATN { if (!esql_parser.__ATN) { @@ -2210,62 +2178,10 @@ export class WhereCommandContext extends ParserRuleContext { export class BooleanExpressionContext extends ParserRuleContext { - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_booleanExpression; } - public copyFrom(ctx: BooleanExpressionContext): void { - super.copyFrom(ctx); - } -} -export class LogicalNotContext extends BooleanExpressionContext { - public NOT(): TerminalNode { return this.getToken(esql_parser.NOT, 0); } - public booleanExpression(): BooleanExpressionContext { - return this.getRuleContext(0, BooleanExpressionContext); - } - constructor(ctx: BooleanExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterLogicalNot) { - listener.enterLogicalNot(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitLogicalNot) { - listener.exitLogicalNot(this); - } - } -} -export class BooleanDefaultContext extends BooleanExpressionContext { - public valueExpression(): ValueExpressionContext { - return this.getRuleContext(0, ValueExpressionContext); - } - constructor(ctx: BooleanExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterBooleanDefault) { - listener.enterBooleanDefault(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitBooleanDefault) { - listener.exitBooleanDefault(this); - } - } -} -export class LogicalBinaryContext extends BooleanExpressionContext { public _left: BooleanExpressionContext; public _operator: Token; public _right: BooleanExpressionContext; + public NOT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NOT, 0); } public booleanExpression(): BooleanExpressionContext[]; public booleanExpression(i: number): BooleanExpressionContext; public booleanExpression(i?: number): BooleanExpressionContext | BooleanExpressionContext[] { @@ -2275,100 +2191,59 @@ export class LogicalBinaryContext extends BooleanExpressionContext { return this.getRuleContext(i, BooleanExpressionContext); } } + public valueExpression(): ValueExpressionContext | undefined { + return this.tryGetRuleContext(0, ValueExpressionContext); + } public AND(): TerminalNode | undefined { return this.tryGetToken(esql_parser.AND, 0); } public OR(): TerminalNode | undefined { return this.tryGetToken(esql_parser.OR, 0); } - constructor(ctx: BooleanExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); + constructor(parent: ParserRuleContext | undefined, invokingState: number) { + super(parent, invokingState); } // @Override + public get ruleIndex(): number { return esql_parser.RULE_booleanExpression; } + // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterLogicalBinary) { - listener.enterLogicalBinary(this); + if (listener.enterBooleanExpression) { + listener.enterBooleanExpression(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitLogicalBinary) { - listener.exitLogicalBinary(this); + if (listener.exitBooleanExpression) { + listener.exitBooleanExpression(this); } } } export class ValueExpressionContext extends ParserRuleContext { + public operatorExpression(): OperatorExpressionContext | undefined { + return this.tryGetRuleContext(0, OperatorExpressionContext); + } + public comparison(): ComparisonContext | undefined { + return this.tryGetRuleContext(0, ComparisonContext); + } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override public get ruleIndex(): number { return esql_parser.RULE_valueExpression; } - public copyFrom(ctx: ValueExpressionContext): void { - super.copyFrom(ctx); - } -} -export class ValueFunctionExpressionContext extends ValueExpressionContext { - public functionIdentifier(): FunctionIdentifierContext { - return this.getRuleContext(0, FunctionIdentifierContext); - } - public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } - public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } - public functionExpressionArgument(): FunctionExpressionArgumentContext[]; - public functionExpressionArgument(i: number): FunctionExpressionArgumentContext; - public functionExpressionArgument(i?: number): FunctionExpressionArgumentContext | FunctionExpressionArgumentContext[] { - if (i === undefined) { - return this.getRuleContexts(FunctionExpressionArgumentContext); - } else { - return this.getRuleContext(i, FunctionExpressionArgumentContext); - } - } - public COMMA(): TerminalNode[]; - public COMMA(i: number): TerminalNode; - public COMMA(i?: number): TerminalNode | TerminalNode[] { - if (i === undefined) { - return this.getTokens(esql_parser.COMMA); - } else { - return this.getToken(esql_parser.COMMA, i); - } - } - constructor(ctx: ValueExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterValueFunctionExpression) { - listener.enterValueFunctionExpression(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitValueFunctionExpression) { - listener.exitValueFunctionExpression(this); - } - } -} -export class ValueExpressionDefaultContext extends ValueExpressionContext { - public operatorExpression(): OperatorExpressionContext { - return this.getRuleContext(0, OperatorExpressionContext); - } - constructor(ctx: ValueExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterValueExpressionDefault) { - listener.enterValueExpressionDefault(this); + if (listener.enterValueExpression) { + listener.enterValueExpression(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitValueExpressionDefault) { - listener.exitValueExpressionDefault(this); + if (listener.exitValueExpression) { + listener.exitValueExpression(this); } } } -export class ComparisonContext extends ValueExpressionContext { + + +export class ComparisonContext extends ParserRuleContext { public _left: OperatorExpressionContext; public _right: OperatorExpressionContext; public comparisonOperator(): ComparisonOperatorContext { @@ -2383,11 +2258,12 @@ export class ComparisonContext extends ValueExpressionContext { return this.getRuleContext(i, OperatorExpressionContext); } } - constructor(ctx: ValueExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); + constructor(parent: ParserRuleContext | undefined, invokingState: number) { + super(parent, invokingState); } // @Override + public get ruleIndex(): number { return esql_parser.RULE_comparison; } + // @Override public enterRule(listener: esql_parserListener): void { if (listener.enterComparison) { listener.enterComparison(this); @@ -2402,65 +2278,60 @@ export class ComparisonContext extends ValueExpressionContext { } -export class OperatorExpressionContext extends ParserRuleContext { - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_operatorExpression; } - public copyFrom(ctx: OperatorExpressionContext): void { - super.copyFrom(ctx); - } -} -export class OperatorExpressionDefaultContext extends OperatorExpressionContext { - public primaryExpression(): PrimaryExpressionContext { - return this.getRuleContext(0, PrimaryExpressionContext); - } - constructor(ctx: OperatorExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); +export class MathFnContext extends ParserRuleContext { + public functionIdentifier(): FunctionIdentifierContext { + return this.getRuleContext(0, FunctionIdentifierContext); } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterOperatorExpressionDefault) { - listener.enterOperatorExpressionDefault(this); + public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } + public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } + public functionExpressionArgument(): FunctionExpressionArgumentContext[]; + public functionExpressionArgument(i: number): FunctionExpressionArgumentContext; + public functionExpressionArgument(i?: number): FunctionExpressionArgumentContext | FunctionExpressionArgumentContext[] { + if (i === undefined) { + return this.getRuleContexts(FunctionExpressionArgumentContext); + } else { + return this.getRuleContext(i, FunctionExpressionArgumentContext); } } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitOperatorExpressionDefault) { - listener.exitOperatorExpressionDefault(this); + public COMMA(): TerminalNode[]; + public COMMA(i: number): TerminalNode; + public COMMA(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(esql_parser.COMMA); + } else { + return this.getToken(esql_parser.COMMA, i); } } -} -export class ArithmeticUnaryContext extends OperatorExpressionContext { - public _operator: Token; - public operatorExpression(): OperatorExpressionContext { - return this.getRuleContext(0, OperatorExpressionContext); - } - public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } - public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } - constructor(ctx: OperatorExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); + constructor(parent: ParserRuleContext | undefined, invokingState: number) { + super(parent, invokingState); } // @Override + public get ruleIndex(): number { return esql_parser.RULE_mathFn; } + // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterArithmeticUnary) { - listener.enterArithmeticUnary(this); + if (listener.enterMathFn) { + listener.enterMathFn(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitArithmeticUnary) { - listener.exitArithmeticUnary(this); + if (listener.exitMathFn) { + listener.exitMathFn(this); } } } -export class ArithmeticBinaryContext extends OperatorExpressionContext { + + +export class OperatorExpressionContext extends ParserRuleContext { public _left: OperatorExpressionContext; public _operator: Token; public _right: OperatorExpressionContext; + public primaryExpression(): PrimaryExpressionContext | undefined { + return this.tryGetRuleContext(0, PrimaryExpressionContext); + } + public mathFn(): MathFnContext | undefined { + return this.tryGetRuleContext(0, MathFnContext); + } public operatorExpression(): OperatorExpressionContext[]; public operatorExpression(i: number): OperatorExpressionContext; public operatorExpression(i?: number): OperatorExpressionContext | OperatorExpressionContext[] { @@ -2470,111 +2341,39 @@ export class ArithmeticBinaryContext extends OperatorExpressionContext { return this.getRuleContext(i, OperatorExpressionContext); } } + public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } + public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } public ASTERISK(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASTERISK, 0); } public SLASH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SLASH, 0); } public PERCENT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PERCENT, 0); } - public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } - public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } - constructor(ctx: OperatorExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterArithmeticBinary) { - listener.enterArithmeticBinary(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitArithmeticBinary) { - listener.exitArithmeticBinary(this); - } - } -} - - -export class PrimaryExpressionContext extends ParserRuleContext { constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_primaryExpression; } - public copyFrom(ctx: PrimaryExpressionContext): void { - super.copyFrom(ctx); - } -} -export class ConstantDefaultContext extends PrimaryExpressionContext { - public constant(): ConstantContext { - return this.getRuleContext(0, ConstantContext); - } - constructor(ctx: PrimaryExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterConstantDefault) { - listener.enterConstantDefault(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitConstantDefault) { - listener.exitConstantDefault(this); - } - } -} -export class DereferenceContext extends PrimaryExpressionContext { - public qualifiedName(): QualifiedNameContext { - return this.getRuleContext(0, QualifiedNameContext); - } - constructor(ctx: PrimaryExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } + public get ruleIndex(): number { return esql_parser.RULE_operatorExpression; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterDereference) { - listener.enterDereference(this); + if (listener.enterOperatorExpression) { + listener.enterOperatorExpression(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitDereference) { - listener.exitDereference(this); + if (listener.exitOperatorExpression) { + listener.exitOperatorExpression(this); } } } -export class ParenthesizedExpressionContext extends PrimaryExpressionContext { - public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } - public booleanExpression(): BooleanExpressionContext { - return this.getRuleContext(0, BooleanExpressionContext); - } - public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } - constructor(ctx: PrimaryExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterParenthesizedExpression) { - listener.enterParenthesizedExpression(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitParenthesizedExpression) { - listener.exitParenthesizedExpression(this); - } + + +export class PrimaryExpressionContext extends ParserRuleContext { + public constant(): ConstantContext | undefined { + return this.tryGetRuleContext(0, ConstantContext); } -} -export class FunctionExpressionContext extends PrimaryExpressionContext { - public identifier(): IdentifierContext { - return this.getRuleContext(0, IdentifierContext); + public qualifiedName(): QualifiedNameContext | undefined { + return this.tryGetRuleContext(0, QualifiedNameContext); } - public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } - public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } + public LP(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LP, 0); } public booleanExpression(): BooleanExpressionContext[]; public booleanExpression(i: number): BooleanExpressionContext; public booleanExpression(i?: number): BooleanExpressionContext | BooleanExpressionContext[] { @@ -2584,6 +2383,10 @@ export class FunctionExpressionContext extends PrimaryExpressionContext { return this.getRuleContext(i, BooleanExpressionContext); } } + public RP(): TerminalNode | undefined { return this.tryGetToken(esql_parser.RP, 0); } + public identifier(): IdentifierContext | undefined { + return this.tryGetRuleContext(0, IdentifierContext); + } public COMMA(): TerminalNode[]; public COMMA(i: number): TerminalNode; public COMMA(i?: number): TerminalNode | TerminalNode[] { @@ -2593,20 +2396,21 @@ export class FunctionExpressionContext extends PrimaryExpressionContext { return this.getToken(esql_parser.COMMA, i); } } - constructor(ctx: PrimaryExpressionContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); + constructor(parent: ParserRuleContext | undefined, invokingState: number) { + super(parent, invokingState); } // @Override + public get ruleIndex(): number { return esql_parser.RULE_primaryExpression; } + // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterFunctionExpression) { - listener.enterFunctionExpression(this); + if (listener.enterPrimaryExpression) { + listener.enterPrimaryExpression(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitFunctionExpression) { - listener.exitFunctionExpression(this); + if (listener.exitPrimaryExpression) { + listener.exitPrimaryExpression(this); } } } @@ -2677,16 +2481,13 @@ export class FieldsContext extends ParserRuleContext { export class FieldContext extends ParserRuleContext { - public qualifiedName(): QualifiedNameContext | undefined { - return this.tryGetRuleContext(0, QualifiedNameContext); - } - public ASSIGN(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASSIGN, 0); } - public valueExpression(): ValueExpressionContext | undefined { - return this.tryGetRuleContext(0, ValueExpressionContext); + public booleanExpression(): BooleanExpressionContext { + return this.getRuleContext(0, BooleanExpressionContext); } - public booleanExpression(): BooleanExpressionContext | undefined { - return this.tryGetRuleContext(0, BooleanExpressionContext); + public userVariable(): UserVariableContext | undefined { + return this.tryGetRuleContext(0, UserVariableContext); } + public ASSIGN(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASSIGN, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -2707,6 +2508,30 @@ export class FieldContext extends ParserRuleContext { } +export class UserVariableContext extends ParserRuleContext { + public identifier(): IdentifierContext { + return this.getRuleContext(0, IdentifierContext); + } + constructor(parent: ParserRuleContext | undefined, invokingState: number) { + super(parent, invokingState); + } + // @Override + public get ruleIndex(): number { return esql_parser.RULE_userVariable; } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterUserVariable) { + listener.enterUserVariable(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitUserVariable) { + listener.exitUserVariable(this); + } + } +} + + export class FromCommandContext extends ParserRuleContext { public FROM(): TerminalNode { return this.getToken(esql_parser.FROM, 0); } public sourceIdentifier(): SourceIdentifierContext[]; @@ -2953,11 +2778,7 @@ export class IdentifierContext extends ParserRuleContext { export class FunctionIdentifierContext extends ParserRuleContext { - public ROUND_FUNCTION_MATH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ROUND_FUNCTION_MATH, 0); } - public AVG_FUNCTION_MATH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.AVG_FUNCTION_MATH, 0); } - public SUM_FUNCTION_MATH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SUM_FUNCTION_MATH, 0); } - public MIN_FUNCTION_MATH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MIN_FUNCTION_MATH, 0); } - public MAX_FUNCTION_MATH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MAX_FUNCTION_MATH, 0); } + public UNARY_FUNCTION(): TerminalNode { return this.getToken(esql_parser.UNARY_FUNCTION, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -3136,16 +2957,12 @@ export class SortCommandContext extends ParserRuleContext { export class OrderExpressionContext extends ParserRuleContext { - public _ordering: Token; - public _nullOrdering: Token; public booleanExpression(): BooleanExpressionContext { return this.getRuleContext(0, BooleanExpressionContext); } - public NULLS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULLS, 0); } - public ASC(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASC, 0); } - public DESC(): TerminalNode | undefined { return this.tryGetToken(esql_parser.DESC, 0); } - public FIRST(): TerminalNode | undefined { return this.tryGetToken(esql_parser.FIRST, 0); } - public LAST(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LAST, 0); } + public ORDERING(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ORDERING, 0); } + public NULLS_ORDERING(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULLS_ORDERING, 0); } + public NULLS_ORDERING_DIRECTION(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULLS_ORDERING_DIRECTION, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -3240,8 +3057,7 @@ export class ProjectClauseContext extends ParserRuleContext { export class BooleanValueContext extends ParserRuleContext { - public TRUE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.TRUE, 0); } - public FALSE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.FALSE, 0); } + public BOOLEAN_VALUE(): TerminalNode { return this.getToken(esql_parser.BOOLEAN_VALUE, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -3335,12 +3151,7 @@ export class StringContext extends ParserRuleContext { export class ComparisonOperatorContext extends ParserRuleContext { - public EQ(): TerminalNode | undefined { return this.tryGetToken(esql_parser.EQ, 0); } - public NEQ(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NEQ, 0); } - public LT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LT, 0); } - public LTE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LTE, 0); } - public GT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.GT, 0); } - public GTE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.GTE, 0); } + public COMPARISON_OPERATOR(): TerminalNode { return this.getToken(esql_parser.COMPARISON_OPERATOR, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts b/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts index d3a3c68a1394..2b943a8bcff4 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts @@ -4,27 +4,14 @@ import { ParseTreeListener } from "antlr4ts/tree/ParseTreeListener"; -import { ValueFunctionExpressionContext } from "./esql_parser"; -import { ValueExpressionDefaultContext } from "./esql_parser"; -import { ComparisonContext } from "./esql_parser"; import { NullLiteralContext } from "./esql_parser"; import { NumericLiteralContext } from "./esql_parser"; import { BooleanLiteralContext } from "./esql_parser"; import { StringLiteralContext } from "./esql_parser"; import { DecimalLiteralContext } from "./esql_parser"; import { IntegerLiteralContext } from "./esql_parser"; -import { ConstantDefaultContext } from "./esql_parser"; -import { DereferenceContext } from "./esql_parser"; -import { ParenthesizedExpressionContext } from "./esql_parser"; -import { FunctionExpressionContext } from "./esql_parser"; import { SingleCommandQueryContext } from "./esql_parser"; import { CompositeQueryContext } from "./esql_parser"; -import { LogicalNotContext } from "./esql_parser"; -import { BooleanDefaultContext } from "./esql_parser"; -import { LogicalBinaryContext } from "./esql_parser"; -import { OperatorExpressionDefaultContext } from "./esql_parser"; -import { ArithmeticUnaryContext } from "./esql_parser"; -import { ArithmeticBinaryContext } from "./esql_parser"; import { SingleStatementContext } from "./esql_parser"; import { QueryContext } from "./esql_parser"; import { SourceCommandContext } from "./esql_parser"; @@ -32,11 +19,14 @@ import { ProcessingCommandContext } from "./esql_parser"; import { WhereCommandContext } from "./esql_parser"; import { BooleanExpressionContext } from "./esql_parser"; import { ValueExpressionContext } from "./esql_parser"; +import { ComparisonContext } from "./esql_parser"; +import { MathFnContext } from "./esql_parser"; import { OperatorExpressionContext } from "./esql_parser"; import { PrimaryExpressionContext } from "./esql_parser"; import { RowCommandContext } from "./esql_parser"; import { FieldsContext } from "./esql_parser"; import { FieldContext } from "./esql_parser"; +import { UserVariableContext } from "./esql_parser"; import { FromCommandContext } from "./esql_parser"; import { EvalCommandContext } from "./esql_parser"; import { StatsCommandContext } from "./esql_parser"; @@ -65,45 +55,6 @@ import { SubqueryExpressionContext } from "./esql_parser"; * `esql_parser`. */ export interface esql_parserListener extends ParseTreeListener { - /** - * Enter a parse tree produced by the `valueFunctionExpression` - * labeled alternative in `esql_parser.valueExpression`. - * @param ctx the parse tree - */ - enterValueFunctionExpression?: (ctx: ValueFunctionExpressionContext) => void; - /** - * Exit a parse tree produced by the `valueFunctionExpression` - * labeled alternative in `esql_parser.valueExpression`. - * @param ctx the parse tree - */ - exitValueFunctionExpression?: (ctx: ValueFunctionExpressionContext) => void; - - /** - * Enter a parse tree produced by the `valueExpressionDefault` - * labeled alternative in `esql_parser.valueExpression`. - * @param ctx the parse tree - */ - enterValueExpressionDefault?: (ctx: ValueExpressionDefaultContext) => void; - /** - * Exit a parse tree produced by the `valueExpressionDefault` - * labeled alternative in `esql_parser.valueExpression`. - * @param ctx the parse tree - */ - exitValueExpressionDefault?: (ctx: ValueExpressionDefaultContext) => void; - - /** - * Enter a parse tree produced by the `comparison` - * labeled alternative in `esql_parser.valueExpression`. - * @param ctx the parse tree - */ - enterComparison?: (ctx: ComparisonContext) => void; - /** - * Exit a parse tree produced by the `comparison` - * labeled alternative in `esql_parser.valueExpression`. - * @param ctx the parse tree - */ - exitComparison?: (ctx: ComparisonContext) => void; - /** * Enter a parse tree produced by the `nullLiteral` * labeled alternative in `esql_parser.constant`. @@ -182,58 +133,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitIntegerLiteral?: (ctx: IntegerLiteralContext) => void; - /** - * Enter a parse tree produced by the `constantDefault` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - enterConstantDefault?: (ctx: ConstantDefaultContext) => void; - /** - * Exit a parse tree produced by the `constantDefault` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - exitConstantDefault?: (ctx: ConstantDefaultContext) => void; - - /** - * Enter a parse tree produced by the `dereference` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - enterDereference?: (ctx: DereferenceContext) => void; - /** - * Exit a parse tree produced by the `dereference` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - exitDereference?: (ctx: DereferenceContext) => void; - - /** - * Enter a parse tree produced by the `parenthesizedExpression` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - enterParenthesizedExpression?: (ctx: ParenthesizedExpressionContext) => void; - /** - * Exit a parse tree produced by the `parenthesizedExpression` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - exitParenthesizedExpression?: (ctx: ParenthesizedExpressionContext) => void; - - /** - * Enter a parse tree produced by the `functionExpression` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - enterFunctionExpression?: (ctx: FunctionExpressionContext) => void; - /** - * Exit a parse tree produced by the `functionExpression` - * labeled alternative in `esql_parser.primaryExpression`. - * @param ctx the parse tree - */ - exitFunctionExpression?: (ctx: FunctionExpressionContext) => void; - /** * Enter a parse tree produced by the `singleCommandQuery` * labeled alternative in `esql_parser.query`. @@ -260,84 +159,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitCompositeQuery?: (ctx: CompositeQueryContext) => void; - /** - * Enter a parse tree produced by the `logicalNot` - * labeled alternative in `esql_parser.booleanExpression`. - * @param ctx the parse tree - */ - enterLogicalNot?: (ctx: LogicalNotContext) => void; - /** - * Exit a parse tree produced by the `logicalNot` - * labeled alternative in `esql_parser.booleanExpression`. - * @param ctx the parse tree - */ - exitLogicalNot?: (ctx: LogicalNotContext) => void; - - /** - * Enter a parse tree produced by the `booleanDefault` - * labeled alternative in `esql_parser.booleanExpression`. - * @param ctx the parse tree - */ - enterBooleanDefault?: (ctx: BooleanDefaultContext) => void; - /** - * Exit a parse tree produced by the `booleanDefault` - * labeled alternative in `esql_parser.booleanExpression`. - * @param ctx the parse tree - */ - exitBooleanDefault?: (ctx: BooleanDefaultContext) => void; - - /** - * Enter a parse tree produced by the `logicalBinary` - * labeled alternative in `esql_parser.booleanExpression`. - * @param ctx the parse tree - */ - enterLogicalBinary?: (ctx: LogicalBinaryContext) => void; - /** - * Exit a parse tree produced by the `logicalBinary` - * labeled alternative in `esql_parser.booleanExpression`. - * @param ctx the parse tree - */ - exitLogicalBinary?: (ctx: LogicalBinaryContext) => void; - - /** - * Enter a parse tree produced by the `operatorExpressionDefault` - * labeled alternative in `esql_parser.operatorExpression`. - * @param ctx the parse tree - */ - enterOperatorExpressionDefault?: (ctx: OperatorExpressionDefaultContext) => void; - /** - * Exit a parse tree produced by the `operatorExpressionDefault` - * labeled alternative in `esql_parser.operatorExpression`. - * @param ctx the parse tree - */ - exitOperatorExpressionDefault?: (ctx: OperatorExpressionDefaultContext) => void; - - /** - * Enter a parse tree produced by the `arithmeticUnary` - * labeled alternative in `esql_parser.operatorExpression`. - * @param ctx the parse tree - */ - enterArithmeticUnary?: (ctx: ArithmeticUnaryContext) => void; - /** - * Exit a parse tree produced by the `arithmeticUnary` - * labeled alternative in `esql_parser.operatorExpression`. - * @param ctx the parse tree - */ - exitArithmeticUnary?: (ctx: ArithmeticUnaryContext) => void; - - /** - * Enter a parse tree produced by the `arithmeticBinary` - * labeled alternative in `esql_parser.operatorExpression`. - * @param ctx the parse tree - */ - enterArithmeticBinary?: (ctx: ArithmeticBinaryContext) => void; - /** - * Exit a parse tree produced by the `arithmeticBinary` - * labeled alternative in `esql_parser.operatorExpression`. - * @param ctx the parse tree - */ - exitArithmeticBinary?: (ctx: ArithmeticBinaryContext) => void; - /** * Enter a parse tree produced by `esql_parser.singleStatement`. * @param ctx the parse tree @@ -415,6 +236,28 @@ export interface esql_parserListener extends ParseTreeListener { */ exitValueExpression?: (ctx: ValueExpressionContext) => void; + /** + * Enter a parse tree produced by `esql_parser.comparison`. + * @param ctx the parse tree + */ + enterComparison?: (ctx: ComparisonContext) => void; + /** + * Exit a parse tree produced by `esql_parser.comparison`. + * @param ctx the parse tree + */ + exitComparison?: (ctx: ComparisonContext) => void; + + /** + * Enter a parse tree produced by `esql_parser.mathFn`. + * @param ctx the parse tree + */ + enterMathFn?: (ctx: MathFnContext) => void; + /** + * Exit a parse tree produced by `esql_parser.mathFn`. + * @param ctx the parse tree + */ + exitMathFn?: (ctx: MathFnContext) => void; + /** * Enter a parse tree produced by `esql_parser.operatorExpression`. * @param ctx the parse tree @@ -470,6 +313,17 @@ export interface esql_parserListener extends ParseTreeListener { */ exitField?: (ctx: FieldContext) => void; + /** + * Enter a parse tree produced by `esql_parser.userVariable`. + * @param ctx the parse tree + */ + enterUserVariable?: (ctx: UserVariableContext) => void; + /** + * Exit a parse tree produced by `esql_parser.userVariable`. + * @param ctx the parse tree + */ + exitUserVariable?: (ctx: UserVariableContext) => void; + /** * Enter a parse tree produced by `esql_parser.fromCommand`. * @param ctx the parse tree diff --git a/packages/kbn-monaco/src/esql/index.ts b/packages/kbn-monaco/src/esql/index.ts index e34fb4917fe0..46ae9fe7f6bc 100644 --- a/packages/kbn-monaco/src/esql/index.ts +++ b/packages/kbn-monaco/src/esql/index.ts @@ -8,5 +8,5 @@ export { ESQL_LANG_ID, ESQL_THEME_ID } from './lib/constants'; export { ESQLLang } from './language'; - +export type { ESQLCustomAutocompleteCallbacks } from './lib/autocomplete/types'; export { buildESQlTheme } from './lib/monaco/esql_theme'; diff --git a/packages/kbn-monaco/src/esql/language.ts b/packages/kbn-monaco/src/esql/language.ts index 6da924ee2f0c..8ab28106460f 100644 --- a/packages/kbn-monaco/src/esql/language.ts +++ b/packages/kbn-monaco/src/esql/language.ts @@ -15,12 +15,15 @@ import type { ESQLWorker } from './worker/esql_worker'; import { DiagnosticsAdapter } from '../common/diagnostics_adapter'; import { WorkerProxyService } from '../common/worker_proxy'; +import { ESQLCompletionAdapter } from './lib/monaco/esql_completion_provider'; +import type { ESQLCustomAutocompleteCallbacks } from './lib/autocomplete/types'; + +const workerProxyService = new WorkerProxyService(); export const ESQLLang: CustomLangModuleType = { ID: ESQL_LANG_ID, async onLanguage() { const { ESQLTokensProvider } = await import('./lib/monaco'); - const workerProxyService = new WorkerProxyService(); workerProxyService.setup(ESQL_LANG_ID); @@ -28,4 +31,8 @@ export const ESQLLang: CustomLangModuleType = { new DiagnosticsAdapter(ESQL_LANG_ID, (...uris) => workerProxyService.getWorker(uris)); }, + + getSuggestionProvider(callbacks?: ESQLCustomAutocompleteCallbacks) { + return new ESQLCompletionAdapter((...uris) => workerProxyService.getWorker(uris), callbacks); + }, }; diff --git a/packages/kbn-monaco/src/esql/lib/antlr_facade.ts b/packages/kbn-monaco/src/esql/lib/antlr_facade.ts index d8c0c1f6e87e..e6bf97e44314 100644 --- a/packages/kbn-monaco/src/esql/lib/antlr_facade.ts +++ b/packages/kbn-monaco/src/esql/lib/antlr_facade.ts @@ -10,10 +10,17 @@ import { CommonTokenStream, CodePointCharStream } from 'antlr4ts'; import { esql_lexer as ESQLLexer } from '../antlr/esql_lexer'; import { esql_parser as ESQLParser } from '../antlr/esql_parser'; +import type { esql_parserListener as ESQLParserListener } from '../antlr/esql_parser_listener'; import type { ANTLREErrorListener } from '../../common/error_listener'; -export const getParser = (inputStream: CodePointCharStream, errorListener: ANTLREErrorListener) => { +export const ROOT_STATEMENT = 'singleStatement'; + +export const getParser = ( + inputStream: CodePointCharStream, + errorListener: ANTLREErrorListener, + parseListener?: ESQLParserListener +) => { const lexer = getLexer(inputStream, errorListener); const tokenStream = new CommonTokenStream(lexer); const parser = new ESQLParser(tokenStream); @@ -21,6 +28,10 @@ export const getParser = (inputStream: CodePointCharStream, errorListener: ANTLR parser.removeErrorListeners(); parser.addErrorListener(errorListener); + if (parseListener) { + parser.addParseListener(parseListener); + } + return parser; }; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/comparison_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/comparison_commands.ts new file mode 100644 index 000000000000..ec8cfe8e596c --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/comparison_commands.ts @@ -0,0 +1,88 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; +import type { AutocompleteCommandDefinition } from '../types'; + +export const comparisonOperatorsCommandsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: 'or', + insertText: 'or', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.orDoc', { + defaultMessage: 'or', + }), + sortText: 'D', + }, + { + label: 'and', + insertText: 'and', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.andDoc', { + defaultMessage: 'and', + }), + sortText: 'D', + }, +]; + +export const comparisonCommandsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: '==', + insertText: '==', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.equalToDoc', { + defaultMessage: 'Equal to', + }), + sortText: 'D', + }, + { + label: '!=', + insertText: '!=', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.notEqualToDoc', { + defaultMessage: 'Not equal to', + }), + sortText: 'D', + }, + { + label: '<', + insertText: '<', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.lessThanDoc', { + defaultMessage: 'Less than', + }), + sortText: 'D', + }, + { + label: '>', + insertText: '>', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.greaterThanDoc', { + defaultMessage: 'Greater than', + }), + sortText: 'D', + }, + { + label: '<=', + insertText: '<=', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.lessThanOrEqualToDoc', { + defaultMessage: 'Less than or equal to', + }), + sortText: 'D', + }, + { + label: '>=', + insertText: '>=', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.greaterThanOrEqualToDoc', { + defaultMessage: 'Greater than or equal to', + }), + sortText: 'D', + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts new file mode 100644 index 000000000000..aa9a9f1777ff --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts @@ -0,0 +1,60 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; +import type { AutocompleteCommandDefinition } from '../types'; + +export const buildFieldsDefinitions = (fields: string[]): AutocompleteCommandDefinition[] => + fields.map((label) => ({ + label, + insertText: label, + kind: 4, + detail: i18n.translate('monaco.esql.autocomplete.fieldDefinition', { + defaultMessage: `Field specified by the input table`, + }), + sortText: 'D', + })); + +export const buildNewVarDefinition = (label: string): AutocompleteCommandDefinition => { + return { + label, + insertText: label, + kind: 21, + detail: i18n.translate('monaco.esql.autocomplete.newVarDoc', { + defaultMessage: 'Define a new variable', + }), + sortText: 'D', + }; +}; + +export const buildSourcesDefinitions = (sources: string[]): AutocompleteCommandDefinition[] => + sources.map((label) => ({ + label, + insertText: label, + kind: 21, + detail: i18n.translate('monaco.esql.autocomplete.sourceDefinition', { + defaultMessage: `Input table`, + }), + sortText: 'A', + })); + +export const buildConstantsDefinitions = ( + userConstants: string[], + detail?: string +): AutocompleteCommandDefinition[] => + userConstants.map((label) => ({ + label, + insertText: label, + kind: 14, + detail: + detail ?? + i18n.translate('monaco.esql.autocomplete.constantDefinition', { + defaultMessage: `User defined variable`, + }), + sortText: 'A', + })); diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/functions_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/functions_commands.ts new file mode 100644 index 000000000000..119a443c4019 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/functions_commands.ts @@ -0,0 +1,87 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; +import { buildDocumentation } from './utils'; + +import type { AutocompleteCommandDefinition } from '../types'; + +export const roundCommandDefinition: AutocompleteCommandDefinition = { + label: 'round', + insertText: 'round', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.roundDoc', { + defaultMessage: + 'Returns a number rounded to the decimal, specified by he closest integer value. The default is to round to an integer.', + }), + documentation: { + value: buildDocumentation('round(grouped[T]): aggregated[T]', [ + 'from index where field="value" | eval rounded = round(field)', + ]), + }, + sortText: 'C', +}; + +export const aggregationFunctionsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: 'avg', + insertText: 'avg', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.avgDoc', { + defaultMessage: 'Returns the average of the values in a field', + }), + documentation: { + value: buildDocumentation('avg(grouped[T]): aggregated[T]', [ + 'from index | stats average = avg(field)', + ]), + }, + sortText: 'C', + }, + { + label: 'max', + insertText: 'max', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.maxDoc', { + defaultMessage: 'Returns the maximum value in a field.', + }), + documentation: { + value: buildDocumentation('max(grouped[T]): aggregated[T]', [ + 'from index | stats max = max(field)', + ]), + }, + sortText: 'C', + }, + { + label: 'min', + insertText: 'min', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.minDoc', { + defaultMessage: 'Returns the minimum value in a field.', + }), + documentation: { + value: buildDocumentation('min(grouped[T]): aggregated[T]', [ + 'from index | stats min = min(field)', + ]), + }, + sortText: 'C', + }, + { + label: 'sum', + insertText: 'sum', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.sumDoc', { + defaultMessage: 'Returns the sum of the values in a field.', + }), + documentation: { + value: buildDocumentation('sum(grouped[T]): aggregated[T]', [ + 'from index | stats sum = sum(field)', + ]), + }, + sortText: 'C', + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/index.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/index.ts new file mode 100644 index 000000000000..ef096d678acc --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/index.ts @@ -0,0 +1,36 @@ +/* + * 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 { aggregationFunctionsDefinitions, roundCommandDefinition } from './functions_commands'; +export { sourceCommandsDefinitions } from './source_commands'; +export { processingCommandsDefinitions, pipeDefinition } from './processing_commands'; + +export { + comparisonCommandsDefinitions, + comparisonOperatorsCommandsDefinitions, +} from './comparison_commands'; +export { + mathOperatorsCommandsDefinitions, + assignOperatorDefinition, + byOperatorDefinition, + openBracketDefinition, + closeBracketDefinition, +} from './operators_commands'; + +export { + orderingCommandsDefinitions, + nullsCommandsDefinition, + nullsOrderingCommandsDefinitions, +} from './ordering_commands'; + +export { + buildNewVarDefinition, + buildSourcesDefinitions, + buildFieldsDefinitions, + buildConstantsDefinitions, +} from './dynamic_commands'; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/operators_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/operators_commands.ts new file mode 100644 index 000000000000..21a5f6260ced --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/operators_commands.ts @@ -0,0 +1,89 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; +import type { AutocompleteCommandDefinition } from '../types'; + +export const byOperatorDefinition: AutocompleteCommandDefinition = { + label: 'by', + insertText: 'by ', + kind: 21, + detail: i18n.translate('monaco.esql.autocomplete.byDoc', { + defaultMessage: 'By', + }), + sortText: 'D', +}; + +export const assignOperatorDefinition: AutocompleteCommandDefinition = { + label: '=', + insertText: '=', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.assignDoc', { + defaultMessage: 'Assign (=)', + }), + sortText: 'D', +}; + +export const openBracketDefinition: AutocompleteCommandDefinition = { + label: '(', + insertText: '(', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.openBracketDoc', { + defaultMessage: 'Open Bracket (', + }), + sortText: 'A', +}; + +export const closeBracketDefinition: AutocompleteCommandDefinition = { + label: ')', + insertText: ')', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.closeBracketDoc', { + defaultMessage: 'Close Bracket )', + }), + sortText: 'A', +}; + +export const mathOperatorsCommandsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: '+', + insertText: '+', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.addDoc', { + defaultMessage: 'Add (+)', + }), + sortText: 'D', + }, + { + label: '-', + insertText: '-', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.subtractDoc', { + defaultMessage: 'Subtract (-)', + }), + sortText: 'D', + }, + { + label: '/', + insertText: '/', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.divideDoc', { + defaultMessage: 'Divide (/)', + }), + sortText: 'D', + }, + { + label: '*', + insertText: '*', + kind: 11, + detail: i18n.translate('monaco.esql.autocomplete.multiplyDoc', { + defaultMessage: 'Multiply (*)', + }), + sortText: 'D', + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/ordering_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/ordering_commands.ts new file mode 100644 index 000000000000..6e932e742a69 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/ordering_commands.ts @@ -0,0 +1,54 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; + +import type { AutocompleteCommandDefinition } from '../types'; + +export const orderingCommandsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: 'asc', + insertText: 'asc', + kind: 17, + detail: i18n.translate('monaco.esql.autocomplete.ascDoc', { + defaultMessage: 'Ascending Order', + }), + sortText: 'D', + }, + { + label: 'desc', + insertText: 'desc', + kind: 17, + detail: i18n.translate('monaco.esql.autocomplete.descDoc', { + defaultMessage: 'Descending Order', + }), + sortText: 'D', + }, +]; + +export const nullsCommandsDefinition: AutocompleteCommandDefinition = { + label: 'nulls', + insertText: 'nulls', + kind: 13, + sortText: 'D', +}; + +export const nullsOrderingCommandsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: 'first', + insertText: 'first', + kind: 13, + sortText: 'D', + }, + { + label: 'last', + insertText: 'last', + kind: 13, + sortText: 'D', + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/processing_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/processing_commands.ts new file mode 100644 index 000000000000..8dbc1ebe3d9c --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/processing_commands.ts @@ -0,0 +1,99 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; +import { buildDocumentation } from './utils'; + +import type { AutocompleteCommandDefinition } from '../types'; + +export const pipeDefinition: AutocompleteCommandDefinition = { + label: '|', + insertText: '|', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.pipeDoc', { + defaultMessage: 'Pipe (|)', + }), + sortText: 'B', +}; + +export const processingCommandsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: 'stats', + insertText: 'stats', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.statsDoc', { + defaultMessage: + 'Calculates aggregate statistics, such as average, count, and sum, over the incoming search results set. Similar to SQL aggregation, if the stats command is used without a BY clause, only one row is returned, which is the aggregation over the entire incoming search results set. When you use a BY clause, one row is returned for each distinct value in the field specified in the BY clause. The stats command returns only the fields in the aggregation, and you can use a wide range of statistical functions with the stats command. When you perform more than one aggregation, separate each aggregation with a comma.', + }), + documentation: { + value: buildDocumentation( + 'stats aggs = fieldSpecification ( `,` fieldSpecification )* ( `by` groups = identifier ( `,` identifier )* )?', + ['… | stats sum(b) by b)', '… | stats avg = avg(a)'] + ), + }, + sortText: 'B', + }, + { + label: 'limit', + insertText: 'limit', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.limitDoc', { + defaultMessage: + 'Returns the first search results, in search order, based on the "limit" specified.', + }), + documentation: { + value: buildDocumentation('limit size = integerLiteral', ['… | limit 100', '… | limit 0']), + }, + sortText: 'B', + }, + { + label: 'eval', + insertText: 'eval', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.evalDoc', { + defaultMessage: + 'Calculates an expression and puts the resulting value into a search results field.', + }), + documentation: { + value: buildDocumentation('eval columns = fieldSpecification ( `,` fieldSpecification )*', [ + '… | eval a = b * c', + ]), + }, + sortText: 'B', + }, + { + label: 'sort', + insertText: 'sort', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.sortDoc', { + defaultMessage: + 'Sorts all results by the specified fields. When in descending order, the results missing a field are considered the smallest possible value of the field, or the largest possible value of the field when in ascending order.', + }), + documentation: { + value: buildDocumentation('sort orders = orderExpression ( `,` orderExpression )*', [ + '… | sort a desc, b nulls last, c asc nulls first', + '… | sort b nulls last`', + '… | sort c asc nulls first`', + ]), + }, + sortText: 'B', + }, + { + label: 'where', + insertText: 'where', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.whereDoc', { + defaultMessage: + 'Uses "predicate-expressions" to filter search results. A predicate expression, when evaluated, returns TRUE or FALSE. The where command only returns the results that evaluate to TRUE. For example, to filter results for a specific field value', + }), + documentation: { + value: buildDocumentation('where condition = expression', ['… | where status_code == 200']), + }, + sortText: 'B', + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/source_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/source_commands.ts new file mode 100644 index 000000000000..a14f776de1bb --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/source_commands.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; +import { buildDocumentation } from './utils'; + +import type { AutocompleteCommandDefinition } from '../types'; + +export const sourceCommandsDefinitions: AutocompleteCommandDefinition[] = [ + { + label: 'from', + insertText: 'from', + kind: 0, + detail: i18n.translate('monaco.esql.autocomplete.fromDoc', { + defaultMessage: + 'Retrieves data from one or more datasets. A dataset is a collection of data that you want to search. The only supported dataset is an index. In a query or subquery, you must use the from command first and it does not need a leading pipe. For example, to retrieve data from an index:', + }), + documentation: { + value: buildDocumentation( + 'from` indexPatterns = wildcardIdentifier (`,` wildcardIdentifier)*', + ['from logs', 'from logs-*', 'from logs_*, events-*', 'from from remote*:logs*'] + ), + }, + sortText: 'A', + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/utils.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/utils.ts new file mode 100644 index 000000000000..87b0c6dc087a --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/utils.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +import { i18n } from '@kbn/i18n'; + +const declarationLabel = i18n.translate('monaco.esql.autocomplete.declarationLabel', { + defaultMessage: 'Declaration:', +}); + +const examplesLabel = i18n.translate('monaco.esql.autocomplete.examplesLabel', { + defaultMessage: 'Examples:', +}); + +/** @internal **/ +export const buildDocumentation = (declaration: string, examples?: string[]) => ` +--- +\ +***${declarationLabel}*** +\ + - \`\`${declaration}\`\` +\ +--- +${ + examples + ? `\ +***${examplesLabel}*** +\ +${examples.map( + (i) => ` + - \`\`${i}\`\` +` +)}` + : '' +}`; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.test.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.test.ts new file mode 100644 index 000000000000..157d111154f1 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.test.ts @@ -0,0 +1,109 @@ +/* + * 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. + */ + +import { CharStreams } from 'antlr4ts'; +import { AutocompleteListener } from './autocomplete_listener'; +import { ANTLREErrorListener } from '../../../common/error_listener'; + +import { getParser, ROOT_STATEMENT } from '../antlr_facade'; + +import { isDynamicAutocompleteItem } from './dymanic_item'; + +describe('autocomplete_listener', () => { + const getAutocompleteSuggestions = (text: string) => { + const errorListener = new ANTLREErrorListener(); + const parseListener = new AutocompleteListener(); + const parser = getParser(CharStreams.fromString(text), errorListener, parseListener); + + parser[ROOT_STATEMENT](); + + return parseListener.getAutocompleteSuggestions(); + }; + + const testSuggestions = (text: string, expected: string[]) => { + test(`${text} => [${expected.join(',')}]`, () => { + const { suggestions } = getAutocompleteSuggestions(text); + expect(suggestions.map((i) => (isDynamicAutocompleteItem(i) ? i : i.label))).toEqual( + expected + ); + }); + }; + + describe('from', () => { + testSuggestions('f', ['from']); + testSuggestions('from ', ['SourceIdentifier']); + testSuggestions('from a,', ['SourceIdentifier']); + testSuggestions('from a, b ', ['|']); + }); + + describe('where', () => { + testSuggestions('from a | where ', ['FieldIdentifier']); + testSuggestions('from a | where "field" ', ['==', '!=', '<', '>', '<=', '>=']); + testSuggestions('from a | where "field" >= ', ['FieldIdentifier']); + testSuggestions('from a | where "field" >= "field1" ', ['or', 'and', '|']); + testSuggestions('from a | where "field" >= "field1" and ', ['FieldIdentifier']); + testSuggestions('from a | where "field" >= "field1" and "field2" ', [ + '==', + '!=', + '<', + '>', + '<=', + '>=', + ]); + testSuggestions('from a | stats a=avg("field") | where a ', ['==', '!=', '<', '>', '<=', '>=']); + testSuggestions('from a | stats a=avg("b") | where "c" ', ['==', '!=', '<', '>', '<=', '>=']); + testSuggestions('from a | where "field" >= "field1" and "field2 == ', ['FieldIdentifier']); + }); + + describe('sort', () => { + testSuggestions('from a | sort ', ['FieldIdentifier']); + testSuggestions('from a | sort "field" ', ['asc', 'desc']); + testSuggestions('from a | sort "field" desc ', ['nulls']); + testSuggestions('from a | sort "field" desc nulls ', ['first', 'last']); + }); + + describe('limit', () => { + testSuggestions('from a | limit ', ['1000']); + testSuggestions('from a | limit 4 ', ['|']); + }); + + describe('stats', () => { + testSuggestions('from a | stats ', ['var0']); + testSuggestions('from a | stats a ', ['=']); + testSuggestions('from a | stats a=', ['avg', 'max', 'min', 'sum', 'FieldIdentifier']); + testSuggestions('from a | stats a=b', ['|', 'by']); + testSuggestions('from a | stats a=b by ', ['FieldIdentifier']); + testSuggestions('from a | stats a=c by d', ['|']); + testSuggestions('from a | stats a=b, ', ['var0']); + testSuggestions('from a | stats a=max', ['(']); + testSuggestions('from a | stats a=min(', ['FieldIdentifier']); + testSuggestions('from a | stats a=min(b', [')', 'FieldIdentifier']); + testSuggestions('from a | stats a=min(b) ', ['|', 'by']); + testSuggestions('from a | stats a=min(b) by ', ['FieldIdentifier']); + testSuggestions('from a | stats a=min(b),', ['var0']); + testSuggestions('from a | stats var0=min(b),var1=c,', ['var2']); + testSuggestions('from a | stats a=min(b), b=max(', ['FieldIdentifier']); + }); + + describe('eval', () => { + testSuggestions('from a | eval ', ['var0']); + testSuggestions('from a | eval a ', ['=']); + testSuggestions('from a | eval a=', ['round', 'FieldIdentifier']); + testSuggestions('from a | eval a=b', ['|', '+', '-', '/', '*']); + testSuggestions('from a | eval a=b, ', ['var0']); + testSuggestions('from a | eval a=round', ['(']); + testSuggestions('from a | eval a=round(', ['FieldIdentifier']); + testSuggestions('from a | eval a=round(b) ', ['|', '+', '-', '/', '*']); + testSuggestions('from a | eval a=round(b),', ['var0']); + testSuggestions('from a | eval a=round(b) +', ['FieldIdentifier']); + testSuggestions('from a | eval a=round(b', [')', 'FieldIdentifier']); + testSuggestions('from a | eval a=round(b), b=round(', ['FieldIdentifier']); + testSuggestions('from a | stats a=round(b), b=round(', ['FieldIdentifier']); + testSuggestions('from a | eval var0=round(b), var1=round(c) | stats ', ['var2']); + }); +}); diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.ts new file mode 100644 index 000000000000..d3cda1712434 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.ts @@ -0,0 +1,316 @@ +/* + * 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. + */ +import type { TerminalNode } from 'antlr4ts/tree/TerminalNode'; +import type { AutocompleteCommandDefinition, UserDefinedVariables } from './types'; +import { DynamicAutocompleteItem } from './dymanic_item'; + +import { esql_parserListener as ESQLParserListener } from '../../antlr/esql_parser_listener'; +import { esql_parser, esql_parser as ESQLParser } from '../../antlr/esql_parser'; + +import { + processingCommandsDefinitions, + sourceCommandsDefinitions, + orderingCommandsDefinitions, + nullsCommandsDefinition, + nullsOrderingCommandsDefinitions, + comparisonCommandsDefinitions, + comparisonOperatorsCommandsDefinitions, + byOperatorDefinition, + pipeDefinition, + openBracketDefinition, + closeBracketDefinition, + mathOperatorsCommandsDefinitions, + aggregationFunctionsDefinitions, + roundCommandDefinition, + assignOperatorDefinition, + buildConstantsDefinitions, + buildNewVarDefinition, +} from './autocomplete_definitions'; + +import { + EvalCommandContext, + StatsCommandContext, + ComparisonContext, + WhereCommandContext, + SourceCommandContext, + OrderExpressionContext, + FieldContext, + QualifiedNameContext, + ProcessingCommandContext, + SourceIdentifierContext, + UserVariableContext, + BooleanExpressionContext, + LimitCommandContext, + ValueExpressionContext, +} from '../../antlr/esql_parser'; + +export class AutocompleteListener implements ESQLParserListener { + private suggestions: Array = []; + private readonly userDefinedVariables: UserDefinedVariables = { + sourceIdentifiers: [], + }; + private readonly tables: string[][] = []; + private parentContext: number | undefined; + + private get fields() { + return this.tables.length > 1 + ? buildConstantsDefinitions(this.tables.at(-2)!) + : [DynamicAutocompleteItem.FieldIdentifier]; + } + + private get hasSuggestions() { + return Boolean(this.suggestions.length); + } + + private isTerminalNodeExists(node: TerminalNode | undefined) { + return node && node.payload?.startIndex >= 0; + } + + private getEndCommandSuggestions(skipDefinitions: AutocompleteCommandDefinition[] = []) { + const suggestions = [pipeDefinition]; + + if ( + !skipDefinitions.find((i) => i === byOperatorDefinition) && + this.parentContext === ESQLParser.STATS + ) { + suggestions.push(byOperatorDefinition); + } + return suggestions; + } + + private getNewVarName() { + const vars = this.tables.flat(); + let index = 0; + + while (true) { + const value = `var${index}`; + if (!vars.includes(value)) { + return value; + } + index++; + } + } + + getAutocompleteSuggestions() { + return { + suggestions: this.suggestions, + userDefinedVariables: this.userDefinedVariables, + }; + } + + /** ESQLParserListener fields **/ + + enterSourceCommand(ctx: SourceCommandContext) { + this.suggestions = []; + } + + exitSourceCommand(ctx: SourceCommandContext) { + if (ctx.exception) { + this.suggestions = sourceCommandsDefinitions; + } else if (!this.hasSuggestions) { + this.suggestions = this.getEndCommandSuggestions(); + } + } + + exitSourceIdentifier(ctx: SourceIdentifierContext) { + if (!ctx.childCount) { + this.suggestions = [DynamicAutocompleteItem.SourceIdentifier]; + } else if (!ctx.exception && ctx.text) { + this.userDefinedVariables.sourceIdentifiers.push(ctx.text); + } + } + + enterProcessingCommand(ctx: ProcessingCommandContext) { + this.tables.push([]); + this.suggestions = []; + this.parentContext = undefined; + } + + exitProcessingCommand(ctx: ProcessingCommandContext) { + if (ctx.exception) { + this.suggestions = processingCommandsDefinitions; + } + this.parentContext = undefined; + } + + enterStatsCommand(ctx: StatsCommandContext) { + this.suggestions = []; + this.parentContext = ESQLParser.STATS; + } + + enterEvalCommand(ctx: EvalCommandContext) { + this.suggestions = []; + this.parentContext = ESQLParser.EVAL; + } + + exitStatsCommand(ctx: StatsCommandContext) { + const qn = ctx.qualifiedNames(); + if (qn && qn.text) { + this.suggestions = this.getEndCommandSuggestions([byOperatorDefinition]); + } + } + + exitQualifiedName(ctx: QualifiedNameContext) { + if ( + ctx + .identifier() + .some( + (i) => + !( + this.isTerminalNodeExists(i.QUOTED_IDENTIFIER()) || + this.isTerminalNodeExists(i.UNQUOTED_IDENTIFIER()) + ) + ) + ) { + this.suggestions = this.fields; + } + } + + enterField(ctx: FieldContext) { + this.suggestions = []; + } + + exitField(ctx: FieldContext) { + const hasAssign = this.isTerminalNodeExists(ctx.ASSIGN()); + + if (ctx.exception) { + if (!hasAssign) { + this.suggestions = [buildNewVarDefinition(this.getNewVarName())]; + return; + } + } else { + if (!hasAssign) { + this.suggestions = [assignOperatorDefinition]; + } + } + } + + exitUserVariable(ctx: UserVariableContext) { + if (!ctx.exception && ctx.text) { + this.tables.at(-1)?.push(ctx.text); + } + } + + enterBooleanExpression(ctx: BooleanExpressionContext) { + this.suggestions = []; + } + + exitBooleanExpression(ctx: BooleanExpressionContext) { + if (ctx.exception) { + const ve = ctx.valueExpression(); + if (!ve) { + if (this.parentContext === ESQLParser.STATS) { + this.suggestions = [...aggregationFunctionsDefinitions, ...this.fields]; + return; + } + + if (this.parentContext === ESQLParser.EVAL) { + this.suggestions = [roundCommandDefinition, ...this.fields]; + return; + } + } + } + } + + exitValueExpression(ctx: ValueExpressionContext) { + const isInStats = this.parentContext === ESQLParser.STATS; + const isInEval = this.parentContext === ESQLParser.EVAL; + + if (this.parentContext && (isInStats || isInEval)) { + const hasFN = ctx.tryGetToken(esql_parser.UNARY_FUNCTION, 0); + const hasLP = ctx.tryGetToken(esql_parser.LP, 0); + const hasRP = ctx.tryGetToken(esql_parser.RP, 0); + + if (hasFN) { + if (!hasLP) { + this.suggestions = [openBracketDefinition]; + return; + } + if (!hasRP) { + if (ctx.childCount === 3) { + this.suggestions = [closeBracketDefinition, ...this.fields]; + return; + } + } + } else { + if (ctx.childCount === 1) { + this.suggestions = [ + ...this.getEndCommandSuggestions(), + ...(isInEval ? mathOperatorsCommandsDefinitions : []), + ]; + return; + } + } + this.suggestions = this.fields; + } + } + + enterWhereCommand(ctx: WhereCommandContext) { + this.suggestions = []; + this.parentContext = ESQLParser.WHERE; + } + + exitWhereCommand(ctx: WhereCommandContext) { + const booleanExpression = ctx.booleanExpression(); + + if (booleanExpression.exception) { + this.suggestions = this.fields; + return; + } else { + const innerBooleanExpressions = booleanExpression.getRuleContexts(BooleanExpressionContext); + if (innerBooleanExpressions.some((be) => be.exception)) { + this.suggestions = this.fields; + return; + } + } + if (!this.hasSuggestions) { + this.suggestions = comparisonCommandsDefinitions; + } + } + + exitComparison(ctx: ComparisonContext) { + const operatorExpression = ctx.operatorExpression(); + if (operatorExpression.some((o) => o.exception)) { + this.suggestions = this.fields; + return; + } + this.suggestions = [ + ...comparisonOperatorsCommandsDefinitions, + ...this.getEndCommandSuggestions(), + ]; + } + + exitOrderExpression(ctx: OrderExpressionContext) { + if (ctx.booleanExpression().exception) { + this.suggestions = this.fields; + return; + } + if (!this.isTerminalNodeExists(ctx.ORDERING())) { + this.suggestions = orderingCommandsDefinitions; + return; + } + if (!this.isTerminalNodeExists(ctx.NULLS_ORDERING())) { + this.suggestions = [nullsCommandsDefinition]; + return; + } + if (!this.isTerminalNodeExists(ctx.NULLS_ORDERING_DIRECTION())) { + this.suggestions = nullsOrderingCommandsDefinitions; + return; + } + } + + exitLimitCommand(ctx: LimitCommandContext) { + const DEFAULT_LIMIT_SIZE = 1000; + + if (!this.isTerminalNodeExists(ctx.INTEGER_LITERAL())) { + this.suggestions = buildConstantsDefinitions([DEFAULT_LIMIT_SIZE.toString()], ''); + } else { + this.suggestions = this.getEndCommandSuggestions(); + } + } +} diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/dymanic_item.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/dymanic_item.ts new file mode 100644 index 000000000000..b819dc34059a --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/dymanic_item.ts @@ -0,0 +1,18 @@ +/* + * 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 enum DynamicAutocompleteItem { + SourceIdentifier = 'SourceIdentifier', + FieldIdentifier = 'FieldIdentifier', +} + +export function isDynamicAutocompleteItem(v: unknown): v is DynamicAutocompleteItem { + return ( + v === DynamicAutocompleteItem.SourceIdentifier || v === DynamicAutocompleteItem.FieldIdentifier + ); +} diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts new file mode 100644 index 000000000000..58438baa298a --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +import { monaco } from '../../../..'; + +/** @public **/ +export interface ESQLCustomAutocompleteCallbacks { + getSourceIdentifiers?: CallbackFn; + getFieldsIdentifiers?: CallbackFn; +} + +/** @internal **/ +type CallbackFn = (ctx: { + word: string; + userDefinedVariables: UserDefinedVariables; +}) => string[] | Promise; + +/** @internal **/ +export interface UserDefinedVariables { + sourceIdentifiers: string[]; +} + +/** @internal **/ +export type AutocompleteCommandDefinition = Pick< + monaco.languages.CompletionItem, + 'label' | 'insertText' | 'kind' | 'detail' | 'documentation' | 'sortText' +>; diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_completion_provider.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_completion_provider.ts new file mode 100644 index 000000000000..40393fe1b844 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_completion_provider.ts @@ -0,0 +1,111 @@ +/* + * 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. + */ + +import { monaco } from '../../../monaco_imports'; +import { DynamicAutocompleteItem, isDynamicAutocompleteItem } from '../autocomplete/dymanic_item'; +import { + buildFieldsDefinitions, + buildSourcesDefinitions, +} from '../autocomplete/autocomplete_definitions/dynamic_commands'; + +import type { + AutocompleteCommandDefinition, + ESQLCustomAutocompleteCallbacks, + UserDefinedVariables, +} from '../autocomplete/types'; +import type { ESQLWorker } from '../../worker/esql_worker'; + +const emptyCompletionList: monaco.languages.CompletionList = { + incomplete: false, + suggestions: [], +}; + +export class ESQLCompletionAdapter implements monaco.languages.CompletionItemProvider { + constructor( + private worker: (...uris: monaco.Uri[]) => Promise, + private callbacks?: ESQLCustomAutocompleteCallbacks + ) {} + + public triggerCharacters = ['(', ' ', '']; + + private async injectDynamicAutocompleteItems( + suggestions: Array, + ctx: { + word: string; + userDefinedVariables: UserDefinedVariables; + } + ): Promise { + let result: AutocompleteCommandDefinition[] = []; + + for (const suggestion of suggestions) { + if (isDynamicAutocompleteItem(suggestion)) { + let dynamicItems: AutocompleteCommandDefinition[] = []; + + if (suggestion === DynamicAutocompleteItem.SourceIdentifier) { + dynamicItems = buildSourcesDefinitions( + (await this.callbacks?.getSourceIdentifiers?.(ctx)) ?? [] + ); + } + + if (suggestion === DynamicAutocompleteItem.FieldIdentifier) { + dynamicItems = buildFieldsDefinitions( + (await this.callbacks?.getFieldsIdentifiers?.(ctx)) ?? [] + ); + } + result = [...result, ...dynamicItems]; + } else { + result = [...result, suggestion]; + } + } + + return result; + } + + async provideCompletionItems( + model: monaco.editor.IReadOnlyModel, + position: monaco.Position + ): Promise { + const lines = model.getLineCount(); + + if ( + lines !== position.lineNumber || + model.getLineContent(position.lineNumber).trimEnd().length >= position.column + ) { + return emptyCompletionList; + } + + const worker = await this.worker(model.uri); + const wordInfo = model.getWordUntilPosition(position); + + const providedSuggestions = await worker.provideAutocompleteSuggestions(model.uri.toString(), { + word: wordInfo.word, + line: position.lineNumber, + index: position.column, + }); + + const withDynamicItems = providedSuggestions + ? await this.injectDynamicAutocompleteItems(providedSuggestions.suggestions, { + word: wordInfo.word, + userDefinedVariables: providedSuggestions.userDefinedVariables, + }) + : []; + + return { + incomplete: true, + suggestions: withDynamicItems.map((i) => ({ + ...i, + range: { + startLineNumber: position.lineNumber, + endLineNumber: position.lineNumber, + startColumn: wordInfo.startColumn, + endColumn: wordInfo.endColumn, + }, + })), + }; + } +} diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts index 7e150ddc7487..94c3c6bbe689 100644 --- a/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts @@ -39,6 +39,14 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ 'quoted_identifier', 'src_ws', 'unquoted_identifier', + 'pipe', + 'not', + 'percent', + 'integer_literal', + 'decimal_literal', + 'src_unquoted_identifier', + 'src_quoted_identifier', + 'string', ], euiThemeVars.euiTextColor ), @@ -52,79 +60,24 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ 'sort', 'by', 'where', - 'unknown_cmd', 'expr_ws', 'row', 'limit', - 'asc', - 'desc', + 'nulls_ordering_direction', + 'nulls_ordering', + 'null', + 'boolean_value', + 'comparison_operator', ], euiThemeVars.euiColorPrimaryText ), // math functions - ...buildRuleGroup( - [ - 'round_function_math', - 'avg_function_math', - 'sum_function_math', - 'min_function_math', - 'max_function_math', - ], - euiThemeVars.euiColorPrimaryText - ), - - // values - ...buildRuleGroup( - [ - 'pipe', - 'true', - 'not', - 'null', - 'nulls', - 'false', - 'src_unquoted_identifier', - 'src_quoted_identifier', - 'string', - ], - euiThemeVars.euiTextColor - ), - - // values #2 - ...buildRuleGroup( - [ - 'true', - 'not', - 'null', - 'nulls', - 'false', - 'not', - 'null', - 'percent', - 'integer_literal', - 'decimal_literal', - ], - euiThemeVars.euiTextColor - ), + ...buildRuleGroup(['unary_function'], euiThemeVars.euiColorPrimaryText), // operators ...buildRuleGroup( - [ - 'or', - 'and', - 'rp', - 'eq', - 'neq', - 'lp', - 'lt', - 'lte', - 'gt', - 'gte', - 'plus', - 'minus', - 'asterisk', - 'slash', - ], + ['or', 'and', 'rp', 'lp', 'plus', 'minus', 'asterisk', 'slash'], euiThemeVars.euiTextSubduedColor ), diff --git a/packages/kbn-monaco/src/esql/worker/esql_worker.ts b/packages/kbn-monaco/src/esql/worker/esql_worker.ts index c83d8707dac6..4d52c2b1094c 100644 --- a/packages/kbn-monaco/src/esql/worker/esql_worker.ts +++ b/packages/kbn-monaco/src/esql/worker/esql_worker.ts @@ -8,8 +8,9 @@ import { CharStreams } from 'antlr4ts'; import { monaco } from '../../monaco_imports'; +import { AutocompleteListener } from '../lib/autocomplete/autocomplete_listener'; import type { BaseWorkerDefinition } from '../../types'; -import { getParser } from '../lib/antlr_facade'; +import { getParser, ROOT_STATEMENT } from '../lib/antlr_facade'; import { ANTLREErrorListener } from '../../common/error_listener'; export class ESQLWorker implements BaseWorkerDefinition { @@ -19,23 +20,47 @@ export class ESQLWorker implements BaseWorkerDefinition { this._ctx = ctx; } - private getTextDocument(modelUri: string): string | undefined { + private getModelCharStream(modelUri: string) { const model = this._ctx.getMirrorModels().find((m) => m.uri.toString() === modelUri); + const text = model?.getValue(); - return model?.getValue(); + if (text) { + return CharStreams.fromString(text); + } } public async getSyntaxErrors(modelUri: string) { - const code = this.getTextDocument(modelUri); + const inputStream = this.getModelCharStream(modelUri); - if (code) { - const inputStream = CharStreams.fromString(code); + if (inputStream) { const errorListener = new ANTLREErrorListener(); const parser = getParser(inputStream, errorListener); - parser.singleStatement(); + parser[ROOT_STATEMENT](); return errorListener.getErrors(); } + return []; + } + + public async provideAutocompleteSuggestions( + modelUri: string, + meta: { + word: string; + line: number; + index: number; + } + ) { + const inputStream = this.getModelCharStream(modelUri); + + if (inputStream) { + const errorListener = new ANTLREErrorListener(); + const parseListener = new AutocompleteListener(); + const parser = getParser(inputStream, errorListener, parseListener); + + parser[ROOT_STATEMENT](); + + return parseListener.getAutocompleteSuggestions(); + } } } diff --git a/packages/kbn-monaco/src/types.ts b/packages/kbn-monaco/src/types.ts index 380f76fb55ad..0e5952db8344 100644 --- a/packages/kbn-monaco/src/types.ts +++ b/packages/kbn-monaco/src/types.ts @@ -7,13 +7,13 @@ */ import type { Observable } from 'rxjs'; - import { monaco } from './monaco_imports'; export interface LangModuleType { ID: string; lexerRules?: monaco.languages.IMonarchLanguage; languageConfiguration?: monaco.languages.LanguageConfiguration; + getSuggestionProvider?: Function; } export interface CompleteLangModuleType extends LangModuleType { From 589a933114f69a28bfb276dec855484183d4a217 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 3 Jan 2023 13:54:17 -0600 Subject: [PATCH 25/30] skip flaky suite (#128836) --- .../index_management/__jest__/a11y/indices_tab.a11y.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts b/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts index e6c071225316..b926cdb6d034 100644 --- a/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts +++ b/x-pack/plugins/index_management/__jest__/a11y/indices_tab.a11y.test.ts @@ -19,7 +19,8 @@ import { createNonDataStreamIndex, } from '../client_integration/home/data_streams_tab.helpers'; -describe('A11y Indices tab', () => { +// FLAKY: https://github.com/elastic/kibana/issues/128836 +describe.skip('A11y Indices tab', () => { let testBed: IndicesTestBed; let httpSetup: ReturnType['httpSetup']; let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; From 2ca590e0068c5389e9340a7c5d4a670a4ea21cfa Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Tue, 3 Jan 2023 20:12:00 +0000 Subject: [PATCH 26/30] Clarify outcome: unknown in audit logging docs (#148153) Resolves #127507 ## Summary Clarify outcome: `unknown` in audit logging docs --- docs/user/security/audit-logging.asciidoc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index b83eea1dc531..b832b21a3eeb 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -313,7 +313,11 @@ Possible values: `deletion` | [[field-event-outcome]] `event.outcome` -| Denotes whether the event represents a success or failure. +a| Denotes whether the event represents a success or failure: + +* Any actions that the user is not authorized to perform are logged with outcome: `failure` +* Authorized read operations are only logged after successfully fetching the data from {es} with outcome: `success` +* Authorized create, update, or delete operations are logged before attempting the operation in {es} with outcome: `unknown` Possible values: `success`, From 750e5e0e956e11eb0e7ab6be301d0132872cb8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 3 Jan 2023 15:38:27 -0500 Subject: [PATCH 27/30] Use docLinks plugin to generate server side URL (#147940) Resolves: https://github.com/elastic/kibana/issues/109937 In this PR, I'm fixing the `Task Manager detected a degradation in performance` log message to provide the proper versioned link to the task manager health monitoring docs. Prior to this PR, it would always point to main / master docs. ## To verify 1. Turn on debug logging in your kibana.yml file ``` logging: loggers: - name: plugins.taskManager level: debug ``` 2. Move this code line outside of the `if (logLevel..` statement => https://github.com/elastic/kibana/blob/4c7ce9d24962913da2207390bc1678280391f9d9/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts#L99 3. Startup Kibana 4. Notice the `Task Manager detected a degradation in performance...` logged 5. Test the URL provided by the log message Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-doc-links/src/get_doc_links.ts | 3 ++ packages/kbn-doc-links/src/types.ts | 26 +++++++++++- .../server/lib/log_health_metrics.test.ts | 40 ++++++++++--------- .../server/lib/log_health_metrics.ts | 12 ++---- x-pack/plugins/task_manager/server/plugin.ts | 2 + .../task_manager/server/routes/health.test.ts | 13 +++++- .../task_manager/server/routes/health.ts | 6 ++- x-pack/plugins/task_manager/tsconfig.json | 1 - 8 files changed, 71 insertions(+), 32 deletions(-) diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index ae5cd305a485..3fe3b54ddb92 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -490,6 +490,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { teamsAction: `${KIBANA_DOCS}teams-action-type.html#configuring-teams`, connectors: `${KIBANA_DOCS}action-types.html`, }, + taskManager: { + healthMonitoring: `${KIBANA_DOCS}task-manager-health-monitoring.html`, + }, maps: { guide: `${KIBANA_DOCS}maps.html`, importGeospatialPrivileges: `${KIBANA_DOCS}import-geospatial-data.html#import-geospatial-privileges`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 68e09877b59e..5d74b66f024f 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -351,7 +351,31 @@ export interface DocLinks { syntheticsCommandReference: string; syntheticsProjectMonitors: string; }>; - readonly alerting: Record; + readonly alerting: Readonly<{ + guide: string; + actionTypes: string; + apmRules: string; + emailAction: string; + emailActionConfig: string; + emailExchangeClientSecretConfig: string; + emailExchangeClientIdConfig: string; + generalSettings: string; + indexAction: string; + esQuery: string; + indexThreshold: string; + pagerDutyAction: string; + preconfiguredConnectors: string; + preconfiguredAlertHistoryConnector: string; + serviceNowAction: string; + serviceNowSIRAction: string; + setupPrerequisites: string; + slackAction: string; + teamsAction: string; + connectors: string; + }>; + readonly taskManager: Readonly<{ + healthMonitoring: string; + }>; readonly maps: Readonly<{ guide: string; importGeospatialPrivileges: string; diff --git a/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts b/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts index 0b8c0e5f42cc..152f0ae82543 100644 --- a/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts +++ b/x-pack/plugins/task_manager/server/lib/log_health_metrics.test.ts @@ -5,7 +5,7 @@ * 2.0. */ import { merge } from 'lodash'; -import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { loggingSystemMock, docLinksServiceMock } from '@kbn/core/server/mocks'; import { configSchema, TaskManagerConfig } from '../config'; import { HealthStatus } from '../monitoring'; import { MonitoredHealth } from '../routes/health'; @@ -18,6 +18,8 @@ jest.mock('./calculate_health_status', () => ({ })); describe('logHealthMetrics', () => { + const docLinks = docLinksServiceMock.create().setup(); + afterEach(() => { const { calculateHealthStatus } = jest.requireMock('./calculate_health_status'); // Reset the last state by running through this as OK @@ -40,16 +42,16 @@ describe('logHealthMetrics', () => { // We must change from OK to Warning (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.OK); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); (calculateHealthStatus as jest.Mock).mockImplementation( () => HealthStatus.Warning ); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); // We must change from OK to Error (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.OK); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.Error); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const debugCalls = (logger as jest.Mocked).debug.mock.calls; const performanceMessage = /^Task Manager detected a degradation in performance/; @@ -78,9 +80,9 @@ describe('logHealthMetrics', () => { (calculateHealthStatus as jest.Mock).mockImplementation( () => HealthStatus.Warning ); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.OK); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); expect((logger as jest.Mocked).warn).not.toHaveBeenCalled(); }); @@ -99,9 +101,9 @@ describe('logHealthMetrics', () => { // We must change from Error to OK (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.Error); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.OK); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); expect((logger as jest.Mocked).warn).not.toHaveBeenCalled(); }); @@ -116,7 +118,7 @@ describe('logHealthMetrics', () => { }); const health = getMockMonitoredHealth(); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const firstDebug = JSON.parse( (logger as jest.Mocked).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '') @@ -135,7 +137,7 @@ describe('logHealthMetrics', () => { }); const health = getMockMonitoredHealth(); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const firstInfo = JSON.parse( (logger as jest.Mocked).info.mock.calls[0][0].replace('Latest Monitored Stats: ', '') @@ -154,7 +156,7 @@ describe('logHealthMetrics', () => { }); const health = getMockMonitoredHealth(); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const firstDebug = JSON.parse( (logger as jest.Mocked).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '') @@ -177,7 +179,7 @@ describe('logHealthMetrics', () => { () => HealthStatus.Warning ); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const logMessage = JSON.parse( ((logger as jest.Mocked).warn.mock.calls[0][0] as string).replace( @@ -201,7 +203,7 @@ describe('logHealthMetrics', () => { const { calculateHealthStatus } = jest.requireMock('./calculate_health_status'); (calculateHealthStatus as jest.Mock).mockImplementation(() => HealthStatus.Error); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const logMessage = JSON.parse( ((logger as jest.Mocked).error.mock.calls[0][0] as string).replace( @@ -241,7 +243,7 @@ describe('logHealthMetrics', () => { }, }); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); expect((logger as jest.Mocked).warn.mock.calls[0][0] as string).toBe( `Detected delay task start of 60s for task(s) \"taskType:test\" (which exceeds configured value of 60s)` @@ -285,7 +287,7 @@ describe('logHealthMetrics', () => { }, }); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); expect((logger as jest.Mocked).warn.mock.calls[0][0] as string).toBe( `Detected delay task start of 60s for task(s) \"taskType:test, taskType:test2\" (which exceeds configured value of 60s)` @@ -317,7 +319,7 @@ describe('logHealthMetrics', () => { stats: {}, }; - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const firstDebug = JSON.parse( (logger as jest.Mocked).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '') @@ -354,7 +356,7 @@ describe('logHealthMetrics', () => { }, }); - logHealthMetrics(health, logger, config, false); + logHealthMetrics(health, logger, config, false, docLinks); const firstDebug = JSON.parse( (logger as jest.Mocked).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '') @@ -379,7 +381,7 @@ describe('logHealthMetrics', () => { }, }); - logHealthMetrics(health, logger, config, true); + logHealthMetrics(health, logger, config, true, docLinks); const { calculateHealthStatus } = jest.requireMock('./calculate_health_status'); expect(calculateHealthStatus).toBeCalledTimes(1); diff --git a/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts b/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts index 65b39ecda4cb..eff71b28f8ab 100644 --- a/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts +++ b/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { kibanaPackageJson } from '@kbn/repo-info'; - import { isEmpty } from 'lodash'; -import { Logger } from '@kbn/core/server'; +import { Logger, DocLinksServiceSetup } from '@kbn/core/server'; import { HealthStatus } from '../monitoring'; import { TaskManagerConfig } from '../config'; import { MonitoredHealth } from '../routes/health'; @@ -29,7 +27,8 @@ export function logHealthMetrics( monitoredHealth: MonitoredHealth, logger: Logger, config: TaskManagerConfig, - shouldRunTasks: boolean + shouldRunTasks: boolean, + docLinks: DocLinksServiceSetup ) { let logLevel: LogLevel = config.monitored_stats_health_verbose_log.level === 'info' ? LogLevel.Info : LogLevel.Debug; @@ -54,10 +53,7 @@ export function logHealthMetrics( } const message = `Latest Monitored Stats: ${JSON.stringify(monitoredHealth)}`; - // TODO: remove when docs support "main" - const docsBranch = kibanaPackageJson.branch === 'main' ? 'master' : 'main'; - - const docLink = `https://www.elastic.co/guide/en/kibana/${docsBranch}/task-manager-health-monitoring.html`; + const docLink = docLinks.links.taskManager.healthMonitoring; const detectedProblemMessage = `Task Manager detected a degradation in performance. This is usually temporary, and Kibana can recover automatically. If the problem persists, check the docs for troubleshooting information: ${docLink} .`; // Drift looks at runtime stats which are not available when task manager is not running tasks diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 3ee6bfb133b5..7f2f537a2528 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -135,6 +135,7 @@ export class TaskManagerPlugin getClusterClient: () => startServicesPromise.then(({ elasticsearch }) => elasticsearch.client), shouldRunTasks: this.shouldRunBackgroundTasks, + docLinks: core.docLinks, }); const monitoredUtilization$ = backgroundTaskUtilizationRoute({ router, @@ -200,6 +201,7 @@ export class TaskManagerPlugin savedObjects, elasticsearch, executionContext, + docLinks, }: CoreStart): TaskManagerStartContract { const savedObjectsRepository = savedObjects.createInternalRepository(['task']); diff --git a/x-pack/plugins/task_manager/server/routes/health.test.ts b/x-pack/plugins/task_manager/server/routes/health.test.ts index f9ad895d0f5d..c20e2df314e8 100644 --- a/x-pack/plugins/task_manager/server/routes/health.test.ts +++ b/x-pack/plugins/task_manager/server/routes/health.test.ts @@ -9,7 +9,7 @@ import { Observable, of, Subject } from 'rxjs'; import { take } from 'rxjs/operators'; import { merge } from 'lodash'; import uuid from 'uuid'; -import { httpServiceMock } from '@kbn/core/server/mocks'; +import { httpServiceMock, docLinksServiceMock } from '@kbn/core/server/mocks'; import { healthRoute } from './health'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { sleep } from '../test_utils'; @@ -47,6 +47,7 @@ const createMockClusterClient = (response: any) => { describe('healthRoute', () => { const logger = loggingSystemMock.create().get(); + const docLinks = docLinksServiceMock.create().setup(); beforeEach(() => { jest.resetAllMocks(); @@ -65,6 +66,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); const [config] = router.get.mock.calls[0]; @@ -88,6 +90,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(mockClusterClient), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); const [, handler] = router.get.mock.calls[0]; @@ -129,6 +132,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(mockClusterClient), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); const [, handler] = router.get.mock.calls[0]; @@ -175,6 +179,7 @@ describe('healthRoute', () => { kibanaIndexName: 'foo', getClusterClient: () => Promise.resolve(mockClusterClient), shouldRunTasks: true, + docLinks, }); const [, handler] = router.get.mock.calls[0]; @@ -218,6 +223,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); stats$.next(mockStat); @@ -278,6 +284,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); stats$.next(warnRuntimeStat); @@ -356,6 +363,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); stats$.next(errorRuntimeStat); @@ -420,6 +428,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); const serviceStatus = getLatest(serviceStatus$); @@ -502,6 +511,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); await sleep(0); @@ -576,6 +586,7 @@ describe('healthRoute', () => { getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()), usageCounter: mockUsageCounter, shouldRunTasks: true, + docLinks, }); await sleep(0); diff --git a/x-pack/plugins/task_manager/server/routes/health.ts b/x-pack/plugins/task_manager/server/routes/health.ts index ba9eaa23c667..9ae3cee2bdcf 100644 --- a/x-pack/plugins/task_manager/server/routes/health.ts +++ b/x-pack/plugins/task_manager/server/routes/health.ts @@ -12,7 +12,7 @@ import { IKibanaResponse, KibanaResponseFactory, } from '@kbn/core/server'; -import { IClusterClient } from '@kbn/core/server'; +import { IClusterClient, DocLinksServiceSetup } from '@kbn/core/server'; import { Observable, Subject } from 'rxjs'; import { tap, map } from 'rxjs/operators'; import { throttleTime } from 'rxjs/operators'; @@ -60,6 +60,7 @@ export interface HealthRouteParams { shouldRunTasks: boolean; getClusterClient: () => Promise; usageCounter?: UsageCounter; + docLinks: DocLinksServiceSetup; } export function healthRoute(params: HealthRouteParams): { @@ -77,6 +78,7 @@ export function healthRoute(params: HealthRouteParams): { getClusterClient, usageCounter, shouldRunTasks, + docLinks, } = params; // if "hot" health stats are any more stale than monitored_stats_required_freshness (pollInterval +1s buffer by default) @@ -111,7 +113,7 @@ export function healthRoute(params: HealthRouteParams): { .subscribe(([monitoredHealth, serviceStatus]) => { serviceStatus$.next(serviceStatus); monitoredHealth$.next(monitoredHealth); - logHealthMetrics(monitoredHealth, logger, config, shouldRunTasks); + logHealthMetrics(monitoredHealth, logger, config, shouldRunTasks, docLinks); }); router.get( diff --git a/x-pack/plugins/task_manager/tsconfig.json b/x-pack/plugins/task_manager/tsconfig.json index 60f0874339f8..2dbc39fb7072 100644 --- a/x-pack/plugins/task_manager/tsconfig.json +++ b/x-pack/plugins/task_manager/tsconfig.json @@ -16,7 +16,6 @@ "@kbn/utility-types", "@kbn/safer-lodash-set", "@kbn/es-types", - "@kbn/repo-info", "@kbn/apm-utils", ], "exclude": [ From 36a3d6915c2150b7a75abd5ac74c3202f222cb08 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 3 Jan 2023 16:47:42 -0500 Subject: [PATCH 28/30] [controls] Edit and save example (#147897) Part of https://github.com/elastic/kibana/issues/145428 PR does the following: * Removes ControlsCallout * Cleans up ControlGroupContainer API to avoid leaking Dashboard implementation details * Removes getCreateControlButton method * Removes getCreateTimeSliderControlButton * Removes getToolbarButtons * Adds openAddDataControlFlyout * Add Edit and save example Screen Shot 2022-12-21 at 9 29 21 AM Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- examples/controls_example/public/app.tsx | 3 + .../controls_example/public/edit_example.tsx | 122 +++++++++ .../control_group/control_group_strings.ts | 4 - .../control_group/editor/create_control.tsx | 164 ------------ .../editor/create_time_slider_control.tsx | 48 ---- .../editor/open_add_data_control_flyout.tsx | 106 ++++++++ .../embeddable/control_group_container.tsx | 102 +------- .../controls_callout/controls_callout.scss | 37 --- .../controls_callout/controls_callout.tsx | 76 ------ .../controls_illustration.tsx | 244 ------------------ src/plugins/controls/public/index.ts | 1 - .../dashboard_app/_dashboard_app_strings.ts | 20 ++ .../add_data_control_button.tsx | 34 +++ .../add_time_slider_control_button.tsx | 56 ++++ .../controls_toolbar_button.tsx | 38 +++ .../top_nav/controls_toolbar_button}/index.ts | 4 +- .../top_nav/dashboard_editing_toolbar.tsx | 24 +- .../component/viewport/dashboard_viewport.tsx | 32 +-- src/plugins/dashboard/tsconfig.json | 1 - .../controls/controls_callout.ts | 103 -------- .../apps/dashboard_elements/controls/index.ts | 1 - .../translations/translations/fr-FR.json | 3 - .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - 24 files changed, 403 insertions(+), 826 deletions(-) create mode 100644 examples/controls_example/public/edit_example.tsx delete mode 100644 src/plugins/controls/public/control_group/editor/create_control.tsx delete mode 100644 src/plugins/controls/public/control_group/editor/create_time_slider_control.tsx create mode 100644 src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx delete mode 100644 src/plugins/controls/public/controls_callout/controls_callout.scss delete mode 100644 src/plugins/controls/public/controls_callout/controls_callout.tsx delete mode 100644 src/plugins/controls/public/controls_callout/controls_illustration.tsx create mode 100644 src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx create mode 100644 src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx create mode 100644 src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx rename src/plugins/{controls/public/controls_callout => dashboard/public/dashboard_app/top_nav/controls_toolbar_button}/index.ts (68%) delete mode 100644 test/functional/apps/dashboard_elements/controls/controls_callout.ts diff --git a/examples/controls_example/public/app.tsx b/examples/controls_example/public/app.tsx index e33ea69a0ef6..6b1f9ddb711d 100644 --- a/examples/controls_example/public/app.tsx +++ b/examples/controls_example/public/app.tsx @@ -14,6 +14,7 @@ import { AppMountParameters } from '@kbn/core/public'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { ControlsExampleStartDeps } from './plugin'; import { BasicReduxExample } from './basic_redux_example'; +import { EditExample } from './edit_example'; import { SearchExample } from './search_example'; export const renderApp = async ( @@ -26,6 +27,8 @@ export const renderApp = async ( <> + + ) : ( diff --git a/examples/controls_example/public/edit_example.tsx b/examples/controls_example/public/edit_example.tsx new file mode 100644 index 000000000000..5278c16f8c85 --- /dev/null +++ b/examples/controls_example/public/edit_example.tsx @@ -0,0 +1,122 @@ +/* + * 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. + */ + +import React, { useState } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingContent, + EuiPanel, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; +import { LazyControlGroupRenderer, ControlGroupContainer } from '@kbn/controls-plugin/public'; +import { withSuspense } from '@kbn/presentation-util-plugin/public'; + +const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer); + +const INPUT_KEY = 'kbnControls:saveExample:input'; + +export const EditExample = () => { + const [isSaving, setIsSaving] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [controlGroup, setControlGroup] = useState(); + + async function onSave() { + setIsSaving(true); + + localStorage.setItem(INPUT_KEY, JSON.stringify(controlGroup!.getInput())); + + // simulated async save await + await new Promise((resolve) => setTimeout(resolve, 1000)); + + setIsSaving(false); + } + + async function onLoad() { + setIsLoading(true); + + // simulated async load await + await new Promise((resolve) => setTimeout(resolve, 1000)); + + let input = {}; + const inputAsString = localStorage.getItem(INPUT_KEY); + if (inputAsString) { + try { + input = JSON.parse(inputAsString); + } catch (e) { + // ignore parse errors + } + } + + setIsLoading(false); + return input; + } + + return ( + <> + +

Edit and save example

+
+ +

Customize controls and persist state to local storage.

+
+ + + + + { + controlGroup!.openAddDataControlFlyout(); + }} + > + Add control + + + + + Save + + + + {isLoading ? ( + <> + + + + ) : null} + { + const persistedInput = await onLoad(); + return { + ...initialInput, + ...persistedInput, + viewMode: ViewMode.EDIT, + }; + }} + onLoadComplete={async (newControlGroup) => { + setControlGroup(newControlGroup); + }} + /> + + + ); +}; diff --git a/src/plugins/controls/public/control_group/control_group_strings.ts b/src/plugins/controls/public/control_group/control_group_strings.ts index 381321c876c3..8492e7f0c47c 100644 --- a/src/plugins/controls/public/control_group/control_group_strings.ts +++ b/src/plugins/controls/public/control_group/control_group_strings.ts @@ -9,10 +9,6 @@ import { i18n } from '@kbn/i18n'; export const ControlGroupStrings = { - getControlButtonTitle: () => - i18n.translate('controls.controlGroup.toolbarButtonTitle', { - defaultMessage: 'Controls', - }), emptyState: { getBadge: () => i18n.translate('controls.controlGroup.emptyState.badgeText', { diff --git a/src/plugins/controls/public/control_group/editor/create_control.tsx b/src/plugins/controls/public/control_group/editor/create_control.tsx deleted file mode 100644 index 7b551195e43a..000000000000 --- a/src/plugins/controls/public/control_group/editor/create_control.tsx +++ /dev/null @@ -1,164 +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. - */ - -import { EuiButton, EuiContextMenuItem } from '@elastic/eui'; -import React from 'react'; - -import { OverlayRef } from '@kbn/core/public'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; -import { pluginServices } from '../../services'; -import { ControlEditor } from './control_editor'; -import { ControlGroupStrings } from '../control_group_strings'; -import { ControlWidth, ControlInput, IEditableControlFactory, DataControlInput } from '../../types'; -import { - DEFAULT_CONTROL_WIDTH, - DEFAULT_CONTROL_GROW, -} from '../../../common/control_group/control_group_constants'; -import { setFlyoutRef } from '../embeddable/control_group_container'; - -export type CreateControlButtonTypes = 'toolbar' | 'callout'; -export interface CreateControlButtonProps { - defaultControlWidth?: ControlWidth; - defaultControlGrow?: boolean; - updateDefaultWidth: (defaultControlWidth: ControlWidth) => void; - updateDefaultGrow: (defaultControlGrow: boolean) => void; - addNewEmbeddable: (type: string, input: Omit) => void; - setLastUsedDataViewId?: (newDataViewId: string) => void; - getRelevantDataViewId?: () => string | undefined; - buttonType: CreateControlButtonTypes; - closePopover?: () => void; -} - -interface CreateControlResult { - type: string; - controlInput: Omit; -} - -export const CreateControlButton = ({ - buttonType, - defaultControlWidth, - defaultControlGrow, - addNewEmbeddable, - closePopover, - getRelevantDataViewId, - setLastUsedDataViewId, - updateDefaultWidth, - updateDefaultGrow, -}: CreateControlButtonProps) => { - // Controls Services Context - const { - overlays: { openFlyout, openConfirm }, - controls: { getControlTypes, getControlFactory }, - theme: { theme$ }, - } = pluginServices.getServices(); - - const createNewControl = async () => { - const ControlsServicesProvider = pluginServices.getContextProvider(); - - const initialInputPromise = new Promise((resolve, reject) => { - let inputToReturn: Partial = {}; - - const onCancel = (ref: OverlayRef) => { - if (Object.keys(inputToReturn).length === 0) { - reject(); - ref.close(); - return; - } - openConfirm(ControlGroupStrings.management.discardNewControl.getSubtitle(), { - confirmButtonText: ControlGroupStrings.management.discardNewControl.getConfirm(), - cancelButtonText: ControlGroupStrings.management.discardNewControl.getCancel(), - title: ControlGroupStrings.management.discardNewControl.getTitle(), - buttonColor: 'danger', - }).then((confirmed) => { - if (confirmed) { - reject(); - ref.close(); - } - }); - }; - - const onSave = (ref: OverlayRef, type?: string) => { - if (!type) { - reject(); - ref.close(); - return; - } - - const factory = getControlFactory(type) as IEditableControlFactory; - if (factory.presaveTransformFunction) { - inputToReturn = factory.presaveTransformFunction(inputToReturn); - } - resolve({ type, controlInput: inputToReturn }); - ref.close(); - }; - - const flyoutInstance = openFlyout( - toMountPoint( - - (inputToReturn.title = newTitle)} - updateWidth={updateDefaultWidth} - updateGrow={updateDefaultGrow} - onSave={(type) => onSave(flyoutInstance, type)} - onCancel={() => onCancel(flyoutInstance)} - onTypeEditorChange={(partialInput) => - (inputToReturn = { ...inputToReturn, ...partialInput }) - } - /> - , - { theme$ } - ), - { - 'aria-label': ControlGroupStrings.manageControl.getFlyoutCreateTitle(), - outsideClickCloses: false, - onClose: (flyout) => { - onCancel(flyout); - setFlyoutRef(undefined); - }, - } - ); - setFlyoutRef(flyoutInstance); - }); - - initialInputPromise.then( - async (promise) => { - await addNewEmbeddable(promise.type, promise.controlInput); - }, - () => {} // swallow promise rejection because it can be part of normal flow - ); - }; - - if (getControlTypes().length === 0) return null; - - const commonButtonProps = { - key: 'addControl', - onClick: () => { - createNewControl(); - if (closePopover) { - closePopover(); - } - }, - 'data-test-subj': 'controls-create-button', - 'aria-label': ControlGroupStrings.management.getManageButtonTitle(), - }; - - return buttonType === 'callout' ? ( - - {ControlGroupStrings.emptyState.getAddControlButtonTitle()} - - ) : ( - - {ControlGroupStrings.emptyState.getAddControlButtonTitle()} - - ); -}; diff --git a/src/plugins/controls/public/control_group/editor/create_time_slider_control.tsx b/src/plugins/controls/public/control_group/editor/create_time_slider_control.tsx deleted file mode 100644 index 3688422a2eeb..000000000000 --- a/src/plugins/controls/public/control_group/editor/create_time_slider_control.tsx +++ /dev/null @@ -1,48 +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. - */ - -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { EuiContextMenuItem } from '@elastic/eui'; - -interface Props { - onCreate: () => void; - closePopover?: () => void; - hasTimeSliderControl: boolean; -} - -export const CreateTimeSliderControlButton = ({ - onCreate, - closePopover, - hasTimeSliderControl, -}: Props) => { - return ( - { - onCreate(); - if (closePopover) { - closePopover(); - } - }} - data-test-subj="controls-create-timeslider-button" - disabled={hasTimeSliderControl} - toolTipContent={ - hasTimeSliderControl - ? i18n.translate('controls.controlGroup.onlyOneTimeSliderControlMsg', { - defaultMessage: 'Control group already contains time slider control.', - }) - : null - } - > - {i18n.translate('controls.controlGroup.addTimeSliderControlButtonTitle', { - defaultMessage: 'Add time slider control', - })} - - ); -}; diff --git a/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx new file mode 100644 index 000000000000..1b417f09e27d --- /dev/null +++ b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx @@ -0,0 +1,106 @@ +/* + * 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. + */ + +import React from 'react'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import type { + AddDataControlProps, + AddOptionsListControlProps, + AddRangeSliderControlProps, +} from '../control_group_input_builder'; +import { ControlGroupStrings } from '../control_group_strings'; +import { ControlGroupContainer, setFlyoutRef } from '../embeddable/control_group_container'; +import { pluginServices } from '../../services'; +import { ControlEditor } from './control_editor'; +import { DataControlInput, OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '../..'; +import { IEditableControlFactory } from '../../types'; +import { + DEFAULT_CONTROL_GROW, + DEFAULT_CONTROL_WIDTH, +} from '../../../common/control_group/control_group_constants'; + +export function openAddDataControlFlyout(this: ControlGroupContainer) { + const { + overlays: { openFlyout, openConfirm }, + controls: { getControlFactory }, + theme: { theme$ }, + } = pluginServices.getServices(); + const ControlsServicesProvider = pluginServices.getContextProvider(); + + let controlInput: Partial = {}; + const onCancel = () => { + if (Object.keys(controlInput).length === 0) { + this.closeAllFlyouts(); + return; + } + + openConfirm(ControlGroupStrings.management.discardNewControl.getSubtitle(), { + confirmButtonText: ControlGroupStrings.management.discardNewControl.getConfirm(), + cancelButtonText: ControlGroupStrings.management.discardNewControl.getCancel(), + title: ControlGroupStrings.management.discardNewControl.getTitle(), + buttonColor: 'danger', + }).then((confirmed) => { + if (confirmed) { + this.closeAllFlyouts(); + } + }); + }; + + const flyoutInstance = openFlyout( + toMountPoint( + + this.setLastUsedDataViewId(newId)} + getRelevantDataViewId={this.getMostRelevantDataViewId} + isCreate={true} + width={this.getInput().defaultControlWidth ?? DEFAULT_CONTROL_WIDTH} + grow={this.getInput().defaultControlGrow ?? DEFAULT_CONTROL_GROW} + updateTitle={(newTitle) => (controlInput.title = newTitle)} + updateWidth={(defaultControlWidth) => this.updateInput({ defaultControlWidth })} + updateGrow={(defaultControlGrow: boolean) => this.updateInput({ defaultControlGrow })} + onSave={(type) => { + this.closeAllFlyouts(); + if (!type) { + return; + } + + const factory = getControlFactory(type) as IEditableControlFactory; + if (factory.presaveTransformFunction) { + controlInput = factory.presaveTransformFunction(controlInput); + } + + if (type === OPTIONS_LIST_CONTROL) { + this.addOptionsListControl(controlInput as AddOptionsListControlProps); + return; + } + + if (type === RANGE_SLIDER_CONTROL) { + this.addRangeSliderControl(controlInput as AddRangeSliderControlProps); + return; + } + + this.addDataControlFromField(controlInput as AddDataControlProps); + }} + onCancel={onCancel} + onTypeEditorChange={(partialInput) => + (controlInput = { ...controlInput, ...partialInput }) + } + /> + , + { theme$ } + ), + { + 'aria-label': ControlGroupStrings.manageControl.getFlyoutCreateTitle(), + outsideClickCloses: false, + onClose: () => { + onCancel(); + }, + } + ); + setFlyoutRef(flyoutInstance); +} diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx index 46774a0747b8..703468dd2da7 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx @@ -12,13 +12,8 @@ import ReactDOM from 'react-dom'; import { compareFilters, COMPARE_ALL_OPTIONS, Filter, uniqFilters } from '@kbn/es-query'; import { BehaviorSubject, merge, Subject, Subscription } from 'rxjs'; import _ from 'lodash'; -import { EuiContextMenuPanel } from '@elastic/eui'; -import { - ReduxEmbeddablePackage, - ReduxEmbeddableTools, - SolutionToolbarPopover, -} from '@kbn/presentation-util-plugin/public'; +import { ReduxEmbeddablePackage, ReduxEmbeddableTools } from '@kbn/presentation-util-plugin/public'; import { OverlayRef } from '@kbn/core/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { Container, EmbeddableFactory } from '@kbn/embeddable-plugin/public'; @@ -36,14 +31,11 @@ import { controlOrdersAreEqual, } from './control_group_chaining_system'; import { pluginServices } from '../../services'; -import { ControlGroupStrings } from '../control_group_strings'; +import { openAddDataControlFlyout } from '../editor/open_add_data_control_flyout'; import { EditControlGroup } from '../editor/edit_control_group'; import { ControlGroup } from '../component/control_group_component'; import { controlGroupReducers } from '../state/control_group_reducers'; -import { OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL, TIME_SLIDER_CONTROL } from '../..'; import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; -import { CreateControlButton, CreateControlButtonTypes } from '../editor/create_control'; -import { CreateTimeSliderControlButton } from '../editor/create_time_slider_control'; import { getNextPanelOrder } from './control_group_helpers'; import type { AddDataControlProps, @@ -127,68 +119,9 @@ export class ControlGroupContainer extends Container< return this.createAndSaveEmbeddable(panelState.type, panelState); } - /** - * Returns a button that allows controls to be created externally using the embeddable - * @param buttonType Controls the button styling - * @param closePopover Closes the create control menu popover when flyout opens - only necessary if `buttonType === 'toolbar'` - * @return If `buttonType == 'toolbar'`, returns `EuiContextMenuPanel` with input control types as items. - * Otherwise, if `buttonType == 'callout'` returns `EuiButton` with popover containing input control types. - */ - public getCreateControlButton = ( - buttonType: CreateControlButtonTypes, - closePopover?: () => void - ) => { - const ControlsServicesProvider = pluginServices.getContextProvider(); - - return ( - - this.updateInput({ defaultControlWidth })} - updateDefaultGrow={(defaultControlGrow: boolean) => - this.updateInput({ defaultControlGrow }) - } - addNewEmbeddable={(type, input) => { - if (type === OPTIONS_LIST_CONTROL) { - this.addOptionsListControl(input as AddOptionsListControlProps); - return; - } - - if (type === RANGE_SLIDER_CONTROL) { - this.addRangeSliderControl(input as AddRangeSliderControlProps); - return; - } - - this.addDataControlFromField(input as AddDataControlProps); - }} - closePopover={closePopover} - getRelevantDataViewId={() => this.getMostRelevantDataViewId()} - setLastUsedDataViewId={(newId) => this.setLastUsedDataViewId(newId)} - /> - - ); - }; - - public getCreateTimeSliderControlButton = (closePopover?: () => void) => { - const childIds = this.getChildIds(); - const hasTimeSliderControl = childIds.some((id) => { - const child = this.getChild(id); - return child.type === TIME_SLIDER_CONTROL; - }); - return ( - { - this.addTimeSliderControl(); - }} - closePopover={closePopover} - hasTimeSliderControl={hasTimeSliderControl} - /> - ); - }; + public openAddDataControlFlyout = openAddDataControlFlyout; - private getEditControlGroupButton = (closePopover: () => void) => { + public getEditControlGroupButton = (closePopover: () => void) => { const ControlsServicesProvider = pluginServices.getContextProvider(); return ( @@ -198,33 +131,6 @@ export class ControlGroupContainer extends Container< ); }; - /** - * Returns the toolbar button that is used for creating controls and managing control settings - * @return `SolutionToolbarPopover` button for input controls - */ - public getToolbarButtons = () => { - return ( - - {({ closePopover }: { closePopover: () => void }) => ( - - )} - - ); - }; - constructor( reduxEmbeddablePackage: ReduxEmbeddablePackage, initialInput: ControlGroupInput, diff --git a/src/plugins/controls/public/controls_callout/controls_callout.scss b/src/plugins/controls/public/controls_callout/controls_callout.scss deleted file mode 100644 index 74add651a523..000000000000 --- a/src/plugins/controls/public/controls_callout/controls_callout.scss +++ /dev/null @@ -1,37 +0,0 @@ -@include euiBreakpoint('xs', 's') { - .controlsIllustration, .emptyStateBadge { - display: none; - } -} - -.controlsWrapper { - &--empty { - display: flex; - overflow: hidden; - margin: 0 $euiSizeS 0 $euiSizeS; - - .addControlButton { - text-align: center; - } - - @include euiBreakpoint('m', 'l', 'xl') { - height: $euiSizeS * 6; - - .emptyStateBadge { - padding-left: $euiSize * 2; - text-transform: uppercase; - } - } - @include euiBreakpoint('xs', 's') { - min-height: $euiSizeS * 6; - - .emptyStateText { - padding-left: 0; - text-align: center; - } - .controlsIllustration__container { - margin-bottom: 0 !important; - } - } - } -} diff --git a/src/plugins/controls/public/controls_callout/controls_callout.tsx b/src/plugins/controls/public/controls_callout/controls_callout.tsx deleted file mode 100644 index b207657cc028..000000000000 --- a/src/plugins/controls/public/controls_callout/controls_callout.tsx +++ /dev/null @@ -1,76 +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. - */ - -import { - EuiBadge, - EuiFlexGroup, - EuiFlexItem, - EuiText, - EuiButtonEmpty, - EuiPanel, -} from '@elastic/eui'; -import React from 'react'; -import useLocalStorage from 'react-use/lib/useLocalStorage'; - -import './controls_callout.scss'; -import { ControlGroupStrings } from '../control_group/control_group_strings'; -import { ControlsIllustration } from './controls_illustration'; - -const CONTROLS_CALLOUT_STATE_KEY = 'dashboard:controlsCalloutDismissed'; - -export interface CalloutProps { - getCreateControlButton?: () => JSX.Element; -} - -export const ControlsCallout = ({ getCreateControlButton }: CalloutProps) => { - const [controlsCalloutDismissed, setControlsCalloutDismissed] = useLocalStorage( - CONTROLS_CALLOUT_STATE_KEY, - false - ); - const dismissControls = () => { - setControlsCalloutDismissed(true); - }; - - if (controlsCalloutDismissed) return null; - - return ( - - - - - - - - - {ControlGroupStrings.emptyState.getBadge()} - - - -

{ControlGroupStrings.emptyState.getCallToAction()}

-
-
- - - {getCreateControlButton && {getCreateControlButton()}} - - - {ControlGroupStrings.emptyState.getDismissButton()} - - - - -
-
-
-
- ); -}; - -// required for dynamic import using React.lazy() -// eslint-disable-next-line import/no-default-export -export default ControlsCallout; diff --git a/src/plugins/controls/public/controls_callout/controls_illustration.tsx b/src/plugins/controls/public/controls_callout/controls_illustration.tsx deleted file mode 100644 index 39d96ee8ad85..000000000000 --- a/src/plugins/controls/public/controls_callout/controls_illustration.tsx +++ /dev/null @@ -1,244 +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. - */ - -import React from 'react'; - -export const ControlsIllustration = () => ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -); diff --git a/src/plugins/controls/public/index.ts b/src/plugins/controls/public/index.ts index 29c2af9ad46f..ff0f139b27a7 100644 --- a/src/plugins/controls/public/index.ts +++ b/src/plugins/controls/public/index.ts @@ -54,7 +54,6 @@ export { type RangeSliderEmbeddableInput, } from './range_slider'; -export { LazyControlsCallout, type CalloutProps } from './controls_callout'; export { LazyControlGroupRenderer, useControlGroupContainerContext, diff --git a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts index e8d0317072e5..f7d710aaa0f9 100644 --- a/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts +++ b/src/plugins/dashboard/public/dashboard_app/_dashboard_app_strings.ts @@ -335,3 +335,23 @@ export const topNavStrings = { }), }, }; + +export const getControlButtonTitle = () => + i18n.translate('dashboard.editingToolbar.controlsButtonTitle', { + defaultMessage: 'Controls', + }); + +export const getAddControlButtonTitle = () => + i18n.translate('dashboard.editingToolbar.addControlButtonTitle', { + defaultMessage: 'Add control', + }); + +export const getOnlyOneTimeSliderControlMsg = () => + i18n.translate('dashboard.editingToolbar.onlyOneTimeSliderControlMsg', { + defaultMessage: 'Control group already contains time slider control.', + }); + +export const getAddTimeSliderControlButtonTitle = () => + i18n.translate('dashboard.editingToolbar.addTimeSliderControlButtonTitle', { + defaultMessage: 'Add time slider control', + }); diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx new file mode 100644 index 000000000000..72c68ce0b063 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_data_control_button.tsx @@ -0,0 +1,34 @@ +/* + * 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. + */ + +import React from 'react'; +import { EuiContextMenuItem } from '@elastic/eui'; +import { ControlGroupContainer } from '@kbn/controls-plugin/public'; +import { getAddControlButtonTitle } from '../../_dashboard_app_strings'; + +interface Props { + closePopover: () => void; + controlGroup: ControlGroupContainer; +} + +export const AddDataControlButton = ({ closePopover, controlGroup }: Props) => { + return ( + { + controlGroup.openAddDataControlFlyout(); + closePopover(); + }} + > + {getAddControlButtonTitle()} + + ); +}; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx new file mode 100644 index 000000000000..d79c7391e580 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/add_time_slider_control_button.tsx @@ -0,0 +1,56 @@ +/* + * 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. + */ + +import React, { useEffect, useState } from 'react'; +import { EuiContextMenuItem } from '@elastic/eui'; +import { ControlGroupContainer, TIME_SLIDER_CONTROL } from '@kbn/controls-plugin/public'; +import { + getAddTimeSliderControlButtonTitle, + getOnlyOneTimeSliderControlMsg, +} from '../../_dashboard_app_strings'; + +interface Props { + closePopover: () => void; + controlGroup: ControlGroupContainer; +} + +export const AddTimeSliderControlButton = ({ closePopover, controlGroup }: Props) => { + const [hasTimeSliderControl, setHasTimeSliderControl] = useState(false); + + useEffect(() => { + const subscription = controlGroup.getInput$().subscribe(() => { + const childIds = controlGroup.getChildIds(); + const nextHasTimeSliderControl = childIds.some((id: string) => { + const child = controlGroup.getChild(id); + return child.type === TIME_SLIDER_CONTROL; + }); + if (nextHasTimeSliderControl !== hasTimeSliderControl) { + setHasTimeSliderControl(nextHasTimeSliderControl); + } + }); + return () => { + subscription.unsubscribe(); + }; + }, [controlGroup, hasTimeSliderControl, setHasTimeSliderControl]); + + return ( + { + controlGroup.addTimeSliderControl(); + closePopover(); + }} + data-test-subj="controls-create-timeslider-button" + disabled={hasTimeSliderControl} + toolTipContent={hasTimeSliderControl ? getOnlyOneTimeSliderControlMsg() : null} + > + {getAddTimeSliderControlButtonTitle()} + + ); +}; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx new file mode 100644 index 000000000000..837fa7c28f31 --- /dev/null +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/controls_toolbar_button.tsx @@ -0,0 +1,38 @@ +/* + * 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. + */ + +import React from 'react'; +import { EuiContextMenuPanel } from '@elastic/eui'; +import { SolutionToolbarPopover } from '@kbn/presentation-util-plugin/public'; +import type { ControlGroupContainer } from '@kbn/controls-plugin/public'; +import { getControlButtonTitle } from '../../_dashboard_app_strings'; +import { AddDataControlButton } from './add_data_control_button'; +import { AddTimeSliderControlButton } from './add_time_slider_control_button'; + +export function ControlsToolbarButton({ controlGroup }: { controlGroup: ControlGroupContainer }) { + return ( + + {({ closePopover }: { closePopover: () => void }) => ( + , + , + controlGroup.getEditControlGroupButton(closePopover), + ]} + /> + )} + + ); +} diff --git a/src/plugins/controls/public/controls_callout/index.ts b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/index.ts similarity index 68% rename from src/plugins/controls/public/controls_callout/index.ts rename to src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/index.ts index 5f6c7e553152..725e1de36df8 100644 --- a/src/plugins/controls/public/controls_callout/index.ts +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/controls_toolbar_button/index.ts @@ -6,6 +6,4 @@ * Side Public License, v 1. */ -import React from 'react'; -export const LazyControlsCallout = React.lazy(() => import('./controls_callout')); -export type { CalloutProps } from './controls_callout'; +export { ControlsToolbarButton } from './controls_toolbar_button'; diff --git a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx index 71dd1691400e..45d06f3cca87 100644 --- a/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx +++ b/src/plugins/dashboard/public/dashboard_app/top_nav/dashboard_editing_toolbar.tsx @@ -25,6 +25,7 @@ import { useDashboardContainerContext } from '../../dashboard_container/dashboar import { pluginServices } from '../../services/plugin_services'; import { getCreateVisualizationButtonTitle } from '../_dashboard_app_strings'; import { EditorMenu } from './editor_menu'; +import { ControlsToolbarButton } from './controls_toolbar_button'; export function DashboardEditingToolbar() { const { @@ -165,6 +166,17 @@ export function DashboardEditingToolbar() { .map(getVisTypeQuickButton) .filter((button) => button) as QuickButtonProps[]; + const extraButtons = [ + , + dashboardContainer.addFromLibrary()} + data-test-subj="dashboardAddPanelButton" + />, + ]; + if (dashboardContainer.controlGroup) { + extraButtons.push(); + } + return ( <> @@ -180,17 +192,7 @@ export function DashboardEditingToolbar() { /> ), quickButtonGroup: , - extraButtons: [ - , - dashboardContainer.addFromLibrary()} - data-test-subj="dashboardAddPanelButton" - />, - dashboardContainer.controlGroup?.getToolbarButtons(), - ], + extraButtons, }} diff --git a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx index 607966a5c0b3..72a886c8223d 100644 --- a/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx +++ b/src/plugins/dashboard/public/dashboard_container/component/viewport/dashboard_viewport.tsx @@ -8,10 +8,8 @@ import React, { useEffect, useRef } from 'react'; -import { withSuspense } from '@kbn/shared-ux-utility'; import { ViewMode } from '@kbn/embeddable-plugin/public'; import { ExitFullScreenButton } from '@kbn/shared-ux-button-exit-full-screen'; -import { CalloutProps, LazyControlsCallout } from '@kbn/controls-plugin/public'; import { DashboardGrid } from '../grid'; import { pluginServices } from '../../../services/plugin_services'; @@ -19,15 +17,13 @@ import { DashboardEmptyScreen } from '../empty_screen/dashboard_empty_screen'; import { useDashboardContainerContext } from '../../dashboard_container_renderer'; import { DashboardLoadedInfo } from '../../embeddable/dashboard_container'; -const ControlsCallout = withSuspense(LazyControlsCallout); - export const DashboardViewport = ({ onDataLoaded, }: { onDataLoaded?: (data: DashboardLoadedInfo) => void; }) => { const { - settings: { isProjectEnabledInLabs, uiSettings }, + settings: { isProjectEnabledInLabs }, } = pluginServices.getServices(); const controlsRoot = useRef(null); @@ -60,30 +56,14 @@ export const DashboardViewport = ({ const isEmbeddedExternally = select((state) => state.componentState.isEmbeddedExternally); const controlsEnabled = isProjectEnabledInLabs('labs:dashboard:dashboardControls'); - const hideAnnouncements = Boolean(uiSettings.get('hideAnnouncements')); return ( <> - {controlsEnabled && controlGroup ? ( - <> - {!hideAnnouncements && - viewMode === ViewMode.EDIT && - panelCount !== 0 && - controlCount === 0 ? ( - { - return controlGroup && controlGroup.getCreateControlButton('callout'); - }} - /> - ) : null} - - {viewMode !== ViewMode.PRINT && ( -
0 ? 'dshDashboardViewport-controls' : ''} - ref={controlsRoot} - /> - )} - + {controlsEnabled && controlGroup && viewMode !== ViewMode.PRINT ? ( +
0 ? 'dshDashboardViewport-controls' : ''} + ref={controlsRoot} + /> ) : null}
{ - describe('callout visibility', async () => { - before(async () => { - await dashboard.gotoDashboardLandingPage(); - - await dashboard.clickNewDashboard(); - await timePicker.setDefaultDataRange(); - await dashboard.saveDashboard('Test Controls Callout'); - }); - - describe('does not show the empty control callout on an empty dashboard', async () => { - before(async () => { - const panelCount = await dashboard.getPanelCount(); - if (panelCount > 0) { - const panels = await dashboard.getDashboardPanels(); - for (const panel of panels) { - await dashboardPanelActions.removePanel(panel); - } - await dashboard.clickQuickSave(); - } - }); - - it('in view mode', async () => { - await dashboard.clickCancelOutOfEditMode(); - await testSubjects.missingOrFail('controls-empty'); - }); - - it('in edit mode', async () => { - await dashboard.switchToEditMode(); - await testSubjects.missingOrFail('controls-empty'); - }); - }); - - it('show the empty control callout on a dashboard with panels', async () => { - await dashboard.switchToEditMode(); - const panelCount = await dashboard.getPanelCount(); - if (panelCount < 1) { - await dashboardAddPanel.addVisualization('Rendering-Test:-animal-sounds-pie'); - } - await testSubjects.existOrFail('controls-empty'); - }); - - it('adding control hides the empty control callout', async () => { - await dashboardControls.createControl({ - controlType: OPTIONS_LIST_CONTROL, - dataViewTitle: 'animals-*', - fieldName: 'sound.keyword', - }); - await testSubjects.missingOrFail('controls-empty'); - }); - - it('deleting all controls shows the emoty control callout again', async () => { - await dashboardControls.deleteAllControls(); - await testSubjects.existOrFail('controls-empty'); - }); - - it('hide callout when hide announcement setting is true', async () => { - await dashboard.clickQuickSave(); - await dashboard.gotoDashboardLandingPage(); - await kibanaServer.uiSettings.update({ hideAnnouncements: true }); - await browser.refresh(); - - await dashboard.loadSavedDashboard('Test Controls Callout'); - await dashboard.switchToEditMode(); - await testSubjects.missingOrFail('controls-empty'); - - await kibanaServer.uiSettings.update({ hideAnnouncements: false }); - }); - - after(async () => { - await dashboard.clickCancelOutOfEditMode(); - await dashboard.gotoDashboardLandingPage(); - }); - }); - }); -} diff --git a/test/functional/apps/dashboard_elements/controls/index.ts b/test/functional/apps/dashboard_elements/controls/index.ts index 84f056b1b97a..d3626934a6f3 100644 --- a/test/functional/apps/dashboard_elements/controls/index.ts +++ b/test/functional/apps/dashboard_elements/controls/index.ts @@ -46,7 +46,6 @@ export default function ({ loadTestFile, getService, getPageObjects }: FtrProvid describe('Controls', function () { before(setup); after(teardown); - loadTestFile(require.resolve('./controls_callout')); loadTestFile(require.resolve('./control_group_settings')); loadTestFile(require.resolve('./options_list')); loadTestFile(require.resolve('./range_slider')); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ba4ecbd5dbc8..51ef8b2c984a 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -368,7 +368,6 @@ "controls.optionsList.popover.invalidSelectionsTooltip": "{selectedOptions} {selectedOptions, plural, one {option sélectionnée} other {options sélectionnées}} {selectedOptions, plural, one {est ignorée} other {sont ignorées}}, car {selectedOptions, plural, one {elle n'est plus présente} other {elles ne sont plus présentes}} dans les données.", "controls.rangeSlider.errors.dataViewNotFound": "Impossible de localiser la vue de données : {dataViewId}", "controls.rangeSlider.errors.fieldNotFound": "Impossible de localiser le champ : {fieldName}", - "controls.controlGroup.addTimeSliderControlButtonTitle": "Ajouter un contrôle de curseur temporel", "controls.controlGroup.emptyState.addControlButtonTitle": "Ajouter un contrôle", "controls.controlGroup.emptyState.badgeText": "Nouveauté", "controls.controlGroup.emptyState.callToAction": "Le filtrage des données s'est amélioré grâce aux contrôles, qui vous permettent d'afficher uniquement les données que vous souhaitez explorer.", @@ -426,10 +425,8 @@ "controls.controlGroup.management.query.useAllSearchSettingsTitle": "Assure la synchronisation entre le groupe de contrôle et la barre de requête, en appliquant une plage temporelle, des pilules de filtre et des requêtes de la barre de requête", "controls.controlGroup.management.validate.subtitle": "Ignorez automatiquement toutes les sélections de contrôle qui ne donneraient aucune donnée.", "controls.controlGroup.management.validate.title": "Valider les sélections utilisateur", - "controls.controlGroup.onlyOneTimeSliderControlMsg": "Le groupe de contrôle contient déjà un contrôle de curseur temporel.", "controls.controlGroup.timeSlider.title": "Curseur temporel", "controls.controlGroup.title": "Groupe de contrôle", - "controls.controlGroup.toolbarButtonTitle": "Contrôles", "controls.frame.error.message": "Une erreur s'est produite. En savoir plus", "controls.optionsList.control.excludeExists": "NE PAS", "controls.optionsList.control.negate": "NON", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 32819db411ac..f456e7231baa 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -370,7 +370,6 @@ "controls.optionsList.popover.invalidSelectionsTooltip": "{selectedOptions}個の選択した{selectedOptions, plural, other {オプション}} {selectedOptions, plural, other {が}}無視されます。{selectedOptions, plural, other {オプションが}}データに存在しません。", "controls.rangeSlider.errors.dataViewNotFound": "データビュー{dataViewId}が見つかりませんでした", "controls.rangeSlider.errors.fieldNotFound": "フィールド{fieldName}が見つかりませんでした", - "controls.controlGroup.addTimeSliderControlButtonTitle": "時間スライダーコントロールを追加", "controls.controlGroup.emptyState.addControlButtonTitle": "コントロールを追加", "controls.controlGroup.emptyState.badgeText": "新規", "controls.controlGroup.emptyState.callToAction": "データのフィルタリングはコントロールによって効果的になりました。探索するデータのみを表示できます。", @@ -428,10 +427,8 @@ "controls.controlGroup.management.query.useAllSearchSettingsTitle": "時間範囲、フィルターピル、クエリバーからのクエリを適用して、コントロールグループを常にクエリと同期します", "controls.controlGroup.management.validate.subtitle": "データがないコントロール選択は自動的に無視されます。", "controls.controlGroup.management.validate.title": "ユーザー選択を検証", - "controls.controlGroup.onlyOneTimeSliderControlMsg": "コントロールグループには、すでに時間スライダーコントロールがあります。", "controls.controlGroup.timeSlider.title": "時間スライダー", "controls.controlGroup.title": "コントロールグループ", - "controls.controlGroup.toolbarButtonTitle": "コントロール", "controls.frame.error.message": "エラーが発生しました。続きを読む", "controls.optionsList.control.excludeExists": "DOES NOT", "controls.optionsList.control.negate": "NOT", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e7e670652aac..e613a52e8115 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -370,7 +370,6 @@ "controls.optionsList.popover.invalidSelectionsTooltip": "{selectedOptions} 个选定{selectedOptions, plural, other {选项}} {selectedOptions, plural, other {已}}忽略,因为{selectedOptions, plural, one {其} other {它们}}已不再在数据中。", "controls.rangeSlider.errors.dataViewNotFound": "找不到数据视图:{dataViewId}", "controls.rangeSlider.errors.fieldNotFound": "找不到字段:{fieldName}", - "controls.controlGroup.addTimeSliderControlButtonTitle": "添加时间滑块控件", "controls.controlGroup.emptyState.addControlButtonTitle": "添加控件", "controls.controlGroup.emptyState.badgeText": "新建", "controls.controlGroup.emptyState.callToAction": "使用控件可以更有效地筛选数据,允许您仅显示要浏览的数据。", @@ -428,10 +427,8 @@ "controls.controlGroup.management.query.useAllSearchSettingsTitle": "通过从查询栏应用时间范围、筛选胶囊和查询,使控件组与查询栏保持同步", "controls.controlGroup.management.validate.subtitle": "自动忽略所有不会生成数据的控件选择。", "controls.controlGroup.management.validate.title": "验证用户选择", - "controls.controlGroup.onlyOneTimeSliderControlMsg": "控件组已包含时间滑块控件。", "controls.controlGroup.timeSlider.title": "时间滑块", "controls.controlGroup.title": "控件组", - "controls.controlGroup.toolbarButtonTitle": "控件", "controls.frame.error.message": "发生错误。阅读更多内容", "controls.optionsList.control.excludeExists": "不", "controls.optionsList.control.negate": "非", From 37a6636654648ef5f4e6db363adc244ad75ef65d Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Tue, 3 Jan 2023 16:05:21 -0700 Subject: [PATCH 29/30] [Dashboard] [Controls] Show document count beside options list suggestions (#146241) Closes https://github.com/elastic/kibana/issues/145039 ## Summary This PR adds a badge beside each options list suggestion that displays how many document that value appears in. https://user-images.githubusercontent.com/8698078/209020139-e0f16eae-6643-432b-9c40-f1a52fc7adf4.mov Note that, in the above video, clicking `"Show only selected"` removes the badge regardless of the validity of the selection. As described in https://github.com/elastic/kibana/pull/144867#issuecomment-1320522731 and the related discussion, it is theoretically possible to still show this badge, thus allowing sorting even when `"Show only selected"` is `on`. However, my investigation into this took me on the following journey: 1. Document count **should not** be stored as part of the `explicitInput`, so `selections` should remain a string array 2. To get around this, the document count could be stored as part of the `validSelections` instead, which is part of the component state. That is, `validSelections` would be an `OptionsListSuggestions` object rather than a string array 3. To make (2) happen, we would need to add a secondary API endpoint specifically for translating the `explicitInput` selections array to the `validSelections` object After realizing (3), it became obvious that adding sorting support to `"Show only selected"` was probably not worth the code complexity that is required - so, at least until we get requests for this feature, I opted to not include it as part of this PR. After all, `"Show only selected"` is ultimately meant to make it easier to deselect/manage your selections, so showing the document count + allowing sorting in this case doesn't have much of an impact. For the same reasons, searching is also disabled when `"Show only selected"` is `on`:

> **Note** > The above screenshots and videos are slightly out-of-date. Refer to https://github.com/elastic/kibana/pull/146241#issuecomment-1370125632 to see how the design of the document count was changed to reduce visual clutter. ### Flaky Test Runner - `control_group_chaining.ts`
- `options_list.ts` ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [x] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../control_group_panel_diff_system.ts | 4 +- .../controls/common/options_list/mocks.tsx | 8 +- .../options_list/suggestions_sorting.ts | 11 +- .../controls/common/options_list/types.ts | 12 +- .../public/__stories__/controls.stories.tsx | 7 +- .../components/options_list_control.tsx | 2 +- .../options_list_editor_options.tsx | 14 +- .../components/options_list_popover.test.tsx | 41 +++- .../components/options_list_popover.tsx | 6 +- .../options_list_popover_action_bar.tsx | 6 +- ...ptions_list_popover_invalid_selections.tsx | 8 +- .../options_list_popover_sorting_button.tsx | 37 ++-- .../options_list_popover_suggestions.tsx | 85 ++++++-- .../components/options_list_strings.ts | 35 ++-- .../embeddable/options_list_embeddable.tsx | 2 +- .../options_list/options_list_reducers.ts | 31 ++- .../controls/public/options_list/types.ts | 8 +- .../options_list/options_list.story.ts | 2 +- .../options_list/options_list_queries.test.ts | 100 +++++++-- .../options_list/options_list_queries.ts | 114 ++++++---- .../controls/control_group_chaining.ts | 125 ++++++----- .../controls/options_list.ts | 194 +++++++++++++----- .../page_objects/dashboard_page_controls.ts | 56 ++++- .../translations/translations/fr-FR.json | 4 - .../translations/translations/ja-JP.json | 4 - .../translations/translations/zh-CN.json | 4 - 26 files changed, 635 insertions(+), 285 deletions(-) diff --git a/src/plugins/controls/common/control_group/control_group_panel_diff_system.ts b/src/plugins/controls/common/control_group/control_group_panel_diff_system.ts index c412a5589cc3..2b89bc55ba2c 100644 --- a/src/plugins/controls/common/control_group/control_group_panel_diff_system.ts +++ b/src/plugins/controls/common/control_group/control_group_panel_diff_system.ts @@ -8,7 +8,7 @@ import deepEqual from 'fast-deep-equal'; import { omit, isEqual } from 'lodash'; -import { DEFAULT_SORT } from '../options_list/suggestions_sorting'; +import { OPTIONS_LIST_DEFAULT_SORT } from '../options_list/suggestions_sorting'; import { OptionsListEmbeddableInput, OPTIONS_LIST_CONTROL } from '../options_list/types'; import { ControlPanelState } from './types'; @@ -65,7 +65,7 @@ export const ControlPanelDiffSystems: { Boolean(singleSelectA) === Boolean(singleSelectB) && Boolean(existsSelectedA) === Boolean(existsSelectedB) && Boolean(runPastTimeoutA) === Boolean(runPastTimeoutB) && - deepEqual(sortA ?? DEFAULT_SORT, sortB ?? DEFAULT_SORT) && + deepEqual(sortA ?? OPTIONS_LIST_DEFAULT_SORT, sortB ?? OPTIONS_LIST_DEFAULT_SORT) && isEqual(selectedA ?? [], selectedB ?? []) && deepEqual(inputA, inputB) ); diff --git a/src/plugins/controls/common/options_list/mocks.tsx b/src/plugins/controls/common/options_list/mocks.tsx index ac80ac387396..943e78c370fc 100644 --- a/src/plugins/controls/common/options_list/mocks.tsx +++ b/src/plugins/controls/common/options_list/mocks.tsx @@ -21,7 +21,13 @@ const mockOptionsListComponentState = { ...getDefaultComponentState(), field: undefined, totalCardinality: 0, - availableOptions: ['woof', 'bark', 'meow', 'quack', 'moo'], + availableOptions: { + woof: { doc_count: 100 }, + bark: { doc_count: 75 }, + meow: { doc_count: 50 }, + quack: { doc_count: 25 }, + moo: { doc_count: 5 }, + }, invalidSelections: [], validSelections: [], } as OptionsListComponentState; diff --git a/src/plugins/controls/common/options_list/suggestions_sorting.ts b/src/plugins/controls/common/options_list/suggestions_sorting.ts index 5289beeeb2a2..a66fe1bdf289 100644 --- a/src/plugins/controls/common/options_list/suggestions_sorting.ts +++ b/src/plugins/controls/common/options_list/suggestions_sorting.ts @@ -10,13 +10,14 @@ import { Direction } from '@elastic/eui'; export type OptionsListSortBy = '_count' | '_key'; -export const DEFAULT_SORT: SortingType = { by: '_count', direction: 'desc' }; +export const OPTIONS_LIST_DEFAULT_SORT: OptionsListSortingType = { + by: '_count', + direction: 'desc', +}; -export const sortDirections: Readonly = ['asc', 'desc'] as const; -export type SortDirection = typeof sortDirections[number]; -export interface SortingType { +export interface OptionsListSortingType { by: OptionsListSortBy; - direction: SortDirection; + direction: Direction; } export const getCompatibleSortingTypes = (type?: string): OptionsListSortBy[] => { diff --git a/src/plugins/controls/common/options_list/types.ts b/src/plugins/controls/common/options_list/types.ts index be5f252af3cc..eb1f7b886d42 100644 --- a/src/plugins/controls/common/options_list/types.ts +++ b/src/plugins/controls/common/options_list/types.ts @@ -9,12 +9,13 @@ import { FieldSpec, DataView, RuntimeFieldSpec } from '@kbn/data-views-plugin/common'; import type { Filter, Query, BoolQuery, TimeRange } from '@kbn/es-query'; -import { SortingType } from './suggestions_sorting'; +import { OptionsListSortingType } from './suggestions_sorting'; import { DataControlInput } from '../types'; export const OPTIONS_LIST_CONTROL = 'optionsListControl'; export interface OptionsListEmbeddableInput extends DataControlInput { + sort?: OptionsListSortingType; selectedOptions?: string[]; existsSelected?: boolean; runPastTimeout?: boolean; @@ -22,7 +23,6 @@ export interface OptionsListEmbeddableInput extends DataControlInput { hideExclude?: boolean; hideExists?: boolean; hideSort?: boolean; - sort?: SortingType; exclude?: boolean; } @@ -32,11 +32,15 @@ export type OptionsListField = FieldSpec & { childFieldName?: string; }; +export interface OptionsListSuggestions { + [key: string]: { doc_count: number }; +} + /** * The Options list response is returned from the serverside Options List route. */ export interface OptionsListResponse { - suggestions: string[]; + suggestions: OptionsListSuggestions; totalCardinality: number; invalidSelections?: string[]; } @@ -61,6 +65,7 @@ export type OptionsListRequest = Omit< */ export interface OptionsListRequestBody { runtimeFieldMap?: Record; + sort?: OptionsListSortingType; filters?: Array<{ bool: BoolQuery }>; selectedOptions?: string[]; runPastTimeout?: boolean; @@ -68,6 +73,5 @@ export interface OptionsListRequestBody { textFieldName?: string; searchString?: string; fieldSpec?: FieldSpec; - sort?: SortingType; fieldName: string; } diff --git a/src/plugins/controls/public/__stories__/controls.stories.tsx b/src/plugins/controls/public/__stories__/controls.stories.tsx index e891e3ba3668..02a13125ba49 100644 --- a/src/plugins/controls/public/__stories__/controls.stories.tsx +++ b/src/plugins/controls/public/__stories__/controls.stories.tsx @@ -54,7 +54,12 @@ const storybookStubOptionsListRequest = async ( setTimeout( () => r({ - suggestions: getFlightSearchOptions(request.field.name, request.searchString), + suggestions: getFlightSearchOptions(request.field.name, request.searchString).reduce( + (o, current, index) => { + return { ...o, [current]: { doc_count: index } }; + }, + {} + ), totalCardinality: 100, }), 120 diff --git a/src/plugins/controls/public/options_list/components/options_list_control.tsx b/src/plugins/controls/public/options_list/components/options_list_control.tsx index 1f19382ab506..43742a817e3e 100644 --- a/src/plugins/controls/public/options_list/components/options_list_control.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_control.tsx @@ -93,7 +93,7 @@ export const OptionsListControl = ({ typeaheadSubject }: { typeaheadSubject: Sub ) : ( <> {validSelections && ( - {validSelections?.join(OptionsListStrings.control.getSeparator())} + {validSelections.join(OptionsListStrings.control.getSeparator())} )} {invalidSelections && ( diff --git a/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx b/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx index dcfd98eedf39..8496971d131b 100644 --- a/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx @@ -21,7 +21,7 @@ import { css } from '@emotion/react'; import { getCompatibleSortingTypes, - DEFAULT_SORT, + OPTIONS_LIST_DEFAULT_SORT, OptionsListSortBy, } from '../../../common/options_list/suggestions_sorting'; import { OptionsListStrings } from './options_list_strings'; @@ -48,8 +48,8 @@ export const OptionsListEditorOptions = ({ fieldType, }: ControlEditorProps) => { const [state, setState] = useState({ - sortDirection: initialInput?.sort?.direction ?? DEFAULT_SORT.direction, - sortBy: initialInput?.sort?.by ?? DEFAULT_SORT.by, + sortDirection: initialInput?.sort?.direction ?? OPTIONS_LIST_DEFAULT_SORT.direction, + sortBy: initialInput?.sort?.by ?? OPTIONS_LIST_DEFAULT_SORT.by, runPastTimeout: initialInput?.runPastTimeout, singleSelect: initialInput?.singleSelect, hideExclude: initialInput?.hideExclude, @@ -60,8 +60,12 @@ export const OptionsListEditorOptions = ({ useEffect(() => { // when field type changes, ensure that the selected sort type is still valid if (!getCompatibleSortingTypes(fieldType).includes(state.sortBy)) { - onChange({ sort: DEFAULT_SORT }); - setState((s) => ({ ...s, sortBy: DEFAULT_SORT.by, sortDirection: DEFAULT_SORT.direction })); + onChange({ sort: OPTIONS_LIST_DEFAULT_SORT }); + setState((s) => ({ + ...s, + sortBy: OPTIONS_LIST_DEFAULT_SORT.by, + sortDirection: OPTIONS_LIST_DEFAULT_SORT.direction, + })); } }, [fieldType, onChange, state.sortBy]); diff --git a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx index ef4e0a8ed0b3..b1315be51ae1 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx @@ -66,7 +66,7 @@ describe('Options list popover', () => { }); test('no available options', async () => { - const popover = await mountComponent({ componentState: { availableOptions: [] } }); + const popover = await mountComponent({ componentState: { availableOptions: {} } }); const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); const noOptionsDiv = findTestSubject( availableOptionsDiv, @@ -118,6 +118,43 @@ describe('Options list popover', () => { expect(sortButton.prop('disabled')).toBe(true); }); + test('test single invalid selection', async () => { + const popover = await mountComponent({ + explicitInput: { + selectedOptions: ['bark', 'woof'], + }, + componentState: { + availableOptions: { + bark: { doc_count: 75 }, + }, + validSelections: ['bark'], + invalidSelections: ['woof'], + }, + }); + const validSelection = findTestSubject(popover, 'optionsList-control-selection-bark'); + expect(validSelection.text()).toEqual('bark75'); + const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); + expect(title).toEqual('Ignored selection'); + const invalidSelection = findTestSubject(popover, 'optionsList-control-ignored-selection-woof'); + expect(invalidSelection.text()).toEqual('woof'); + expect(invalidSelection.hasClass('optionsList__selectionInvalid')).toBe(true); + }); + + test('test title when multiple invalid selections', async () => { + const popover = await mountComponent({ + explicitInput: { selectedOptions: ['bark', 'woof', 'meow'] }, + componentState: { + availableOptions: { + bark: { doc_count: 75 }, + }, + validSelections: ['bark'], + invalidSelections: ['woof', 'meow'], + }, + }); + const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); + expect(title).toEqual('Ignored selections'); + }); + test('should default to exclude = false', async () => { const popover = await mountComponent(); const includeButton = findTestSubject(popover, 'optionsList__includeResults'); @@ -172,7 +209,7 @@ describe('Options list popover', () => { test('if existsSelected = false and no suggestions, then "Exists" does not show up', async () => { const popover = await mountComponent({ - componentState: { availableOptions: [] }, + componentState: { availableOptions: {} }, explicitInput: { existsSelected: false }, }); const existsOption = findTestSubject(popover, 'optionsList-control-selection-exists'); diff --git a/src/plugins/controls/public/options_list/components/options_list_popover.tsx b/src/plugins/controls/public/options_list/components/options_list_popover.tsx index bc1e62fccfda..6ad39e0b3dbd 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover.tsx @@ -46,8 +46,8 @@ export const OptionsListPopover = ({ width, updateSearchString }: OptionsListPop return ( {title} @@ -59,10 +59,10 @@ export const OptionsListPopover = ({ width, updateSearchString }: OptionsListPop /> )}
300 ? width : undefined }} className="optionsList __items" - data-option-count={availableOptions?.length ?? 0} + style={{ width: width > 300 ? width : undefined }} data-test-subj={`optionsList-control-available-options`} + data-option-count={Object.keys(availableOptions ?? {}).length} > {!showOnlySelected && invalidSelections && !isEmpty(invalidSelections) && ( diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx index f6be1cb0e7a6..375a2a205869 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx @@ -112,7 +112,11 @@ export const OptionsListPopoverActionBar = ({ display={showOnlySelected ? 'base' : 'empty'} onClick={() => setShowOnlySelected(!showOnlySelected)} data-test-subj="optionsList-control-show-only-selected" - aria-label={OptionsListStrings.popover.getClearAllSelectionsButtonTitle()} + aria-label={ + showOnlySelected + ? OptionsListStrings.popover.getAllOptionsButtonTitle() + : OptionsListStrings.popover.getSelectedOptionsButtonTitle() + } /> diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx index 1a6ec2176dd4..01c9f14363a4 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx @@ -26,11 +26,14 @@ export const OptionsListPopoverInvalidSelections = () => { // Select current state from Redux using multiple selectors to avoid rerenders. const invalidSelections = select((state) => state.componentState.invalidSelections); - return ( <> - +