diff --git a/.backportrc.json b/.backportrc.json index 89eefb2e3c4420..24e08d4768d9af 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -2,6 +2,7 @@ "upstream": "elastic/kibana", "targetBranchChoices": [ { "name": "master", "checked": true }, + "8.0", "7.16", "7.15", "7.14", @@ -32,7 +33,7 @@ ], "targetPRLabels": ["backport"], "branchLabelMapping": { - "^v8.0.0$": "master", + "^v8.1.0$": "master", "^v(\\d+).(\\d+).\\d+$": "$1.$2" }, "autoMerge": true, diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 3337cfb5dfcdd7..b03a46b5b5c667 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -21,7 +21,7 @@ steps: agents: queue: ci-group-6 depends_on: build - timeout_in_minutes: 150 + timeout_in_minutes: 250 key: default-cigroup retry: automatic: diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.js b/.buildkite/scripts/pipelines/pull_request/pipeline.js index 02d6fc270ddb01..c5ed216042b680 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.js +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.js @@ -57,6 +57,9 @@ const uploadPipeline = (pipelineContent) => { if ( (await doAnyChangesMatch([ /^x-pack\/plugins\/security_solution/, + /^x-pack\/plugins\/cases/, + /^x-pack\/plugins\/lists/, + /^x-pack\/plugins\/timelines/, /^x-pack\/test\/security_solution_cypress/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/, diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000000000..31bd822640359d --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,58 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 180 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: false + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: ["Team:apm"] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: ["technical debt", "prevent stale"] + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: stale + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +# closeComment: > +# Your comment here. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed diff --git a/dev_docs/tutorials/saved_objects.mdx b/dev_docs/tutorials/saved_objects.mdx index 29a0b60983d90c..9583e195d1c828 100644 --- a/dev_docs/tutorials/saved_objects.mdx +++ b/dev_docs/tutorials/saved_objects.mdx @@ -254,4 +254,4 @@ the error should be verbose and informative so that the corrupt document can be ### Testing Migrations -Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the . +Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the . diff --git a/docs/api/dashboard-api.asciidoc b/docs/api/dashboard-api.asciidoc index e6f54dd9156ecb..94511c3154fe09 100644 --- a/docs/api/dashboard-api.asciidoc +++ b/docs/api/dashboard-api.asciidoc @@ -1,7 +1,7 @@ [[dashboard-api]] == Import and export dashboard APIs -deprecated::[7.15.0,Both of these APIs have been deprecated in favor of <> and <>.] +deprecated::[7.15.0,These experimental APIs have been deprecated in favor of <> and <>.] Import and export dashboards with the corresponding saved objects, such as visualizations, saved searches, and index patterns. diff --git a/docs/api/dashboard/export-dashboard.asciidoc b/docs/api/dashboard/export-dashboard.asciidoc index 3a20eff0a54d25..098ec976569bd8 100644 --- a/docs/api/dashboard/export-dashboard.asciidoc +++ b/docs/api/dashboard/export-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -Export dashboards and corresponding saved objects. +experimental[] Export dashboards and corresponding saved objects. [[dashboard-api-export-request]] ==== Request diff --git a/docs/api/dashboard/import-dashboard.asciidoc b/docs/api/dashboard/import-dashboard.asciidoc index e4817d6cb7ee9b..41eb47500c8d7a 100644 --- a/docs/api/dashboard/import-dashboard.asciidoc +++ b/docs/api/dashboard/import-dashboard.asciidoc @@ -6,7 +6,7 @@ deprecated::[7.15.0,Use <> instead.] -Import dashboards and corresponding saved objects. +experimental[] Import dashboards and corresponding saved objects. [[dashboard-api-import-request]] ==== Request diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index ed6763db69ffe4..4e44df9d4e1835 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -230,6 +230,7 @@ readonly links: { readonly ingest: Record; readonly fleet: Readonly<{ datastreamsILM: string; + beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 96c2c0df9d7822..5871a84c5402ed 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly elasticStackGetStarted: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
readonly troubleshootGaps: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
elasticsearchEnableApiKeys: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
kibanaDisableLegacyUrlAliasesApi: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
datastreamsILM: string;
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
installElasticAgent: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
learnMoreBlog: string;
apiKeysLearnMore: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly elasticStackGetStarted: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: string;
};
readonly metricbeat: {
readonly base: string;
readonly configure: string;
readonly httpEndpoint: string;
readonly install: string;
readonly start: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly libbeat: {
readonly getStarted: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly composite: string;
readonly composite_missing_bucket: string;
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: {
readonly overview: string;
readonly mapping: string;
};
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessLangSpec: string;
readonly painlessSyntax: string;
readonly painlessWalkthrough: string;
readonly luceneExpressions: string;
};
readonly search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
readonly troubleshootGaps: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
cronExpressions: string;
executeWatchActionModes: string;
indexExists: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
painlessExecuteAPIContexts: string;
putComponentTemplateMetadata: string;
putSnapshotLifecyclePolicy: string;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
elasticsearchEnableApiKeys: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
kibanaDisableLegacyUrlAliasesApi: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
datastreamsILM: string;
beatsAgentComparison: string;
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
installElasticAgent: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
learnMoreBlog: string;
apiKeysLearnMore: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 6bac5e7940dbb3..f2da704760f3b3 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -516,6 +516,9 @@ Enables the legacy charts library for timelion charts in Visualize. **This setting is deprecated and will not be supported as of 8.0.** Maps values to specific colors in charts using the *Compatibility* palette. +[[visualization-uselegacytimeaxis]]`visualization:useLegacyTimeAxis`:: +Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB + [[visualization-heatmap-maxbuckets]]`visualization:heatmap:maxBuckets`:: The maximum number of buckets a datasource can return. High numbers can have a negative impact on your browser rendering performance. diff --git a/docs/settings/spaces-settings.asciidoc b/docs/settings/spaces-settings.asciidoc index 5483912387ceca..dd379431011454 100644 --- a/docs/settings/spaces-settings.asciidoc +++ b/docs/settings/spaces-settings.asciidoc @@ -13,7 +13,7 @@ return all spaces using a single `_search` from {es}, so you must configure this setting lower than the `index.max_result_window` in {es}. The default is `1000`. -`monitoring.cluster_alerts-allowedSpaces` {ess-icon}:: +`monitoring.cluster_alerts.allowedSpaces` {ess-icon}:: Specifies the spaces where cluster alerts are automatically generated. You must specify all spaces where you want to generate alerts, including the default space. When the default space is unspecified, {kib} is unable to generate an alert for the default space. diff --git a/package.json b/package.json index b2065ddc1d6088..0e5c042e4236d4 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dashboarding" ], "private": true, - "version": "8.0.0", + "version": "8.1.0", "branch": "master", "types": "./kibana.d.ts", "tsdocMetadata": "./build/tsdoc-metadata.json", @@ -98,7 +98,7 @@ "@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace", "@elastic/apm-rum": "^5.9.1", "@elastic/apm-rum-react": "^1.3.1", - "@elastic/charts": "38.0.1", + "@elastic/charts": "38.1.0", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.35", "@elastic/ems-client": "8.0.0", diff --git a/packages/elastic-apm-synthtrace/README.md b/packages/elastic-apm-synthtrace/README.md index 3d65120b4b6c2c..cdbd536831676b 100644 --- a/packages/elastic-apm-synthtrace/README.md +++ b/packages/elastic-apm-synthtrace/README.md @@ -93,10 +93,10 @@ const esEvents = toElasticsearchOutput([ Via the CLI, you can upload scenarios, either using a fixed time range or continuously generating data. Some examples are available in in `src/scripts/examples`. Here's an example for live data: -`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-generator/src/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --live` +`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --live` For a fixed time window: -`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-generator/src/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --from=now-24h --to=now` +`$ node packages/elastic-apm-synthtrace/src/scripts/run packages/elastic-apm-synthtrace/src/scripts/examples/01_simple_trace.ts --target=http://admin:changeme@localhost:9200 --from=now-24h --to=now` The script will try to automatically find bootstrapped APM indices. __If these indices do not exist, the script will exit with an error. It will not bootstrap the indices itself.__ diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 453e5c174452d0..99f9c04069b720 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -8,7 +8,7 @@ pageLoadAssetSize: console: 46091 core: 435325 crossClusterReplication: 65408 - dashboard: 186763 + dashboard: 82025 dashboardEnhanced: 65646 devTools: 38637 discover: 99999 @@ -76,7 +76,7 @@ pageLoadAssetSize: watcher: 43598 runtimeFields: 41752 stackAlerts: 29684 - presentationUtil: 94301 + presentationUtil: 84606 osquery: 107090 fileUpload: 25664 dataVisualizer: 27530 diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 2bbb4703ecd19d..0cab7a72adae8b 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -479,6 +479,7 @@ export class DocLinksService { settingsFleetServerHostSettings: `${FLEET_DOCS}fleet-settings.html#fleet-server-hosts-setting`, troubleshooting: `${FLEET_DOCS}fleet-troubleshooting.html`, elasticAgent: `${FLEET_DOCS}elastic-agent-installation.html`, + beatsAgentComparison: `${FLEET_DOCS}beats-agent-comparison.html`, datastreams: `${FLEET_DOCS}data-streams.html`, datastreamsILM: `${FLEET_DOCS}data-streams.html#data-streams-ilm`, datastreamsNamingScheme: `${FLEET_DOCS}data-streams.html#data-streams-naming-scheme`, @@ -736,6 +737,7 @@ export interface DocLinksStart { readonly ingest: Record; readonly fleet: Readonly<{ datastreamsILM: string; + beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index cf0b526aa9fd92..5d63d2b6f77cea 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -697,6 +697,7 @@ export interface DocLinksStart { readonly ingest: Record; readonly fleet: Readonly<{ datastreamsILM: string; + beatsAgentComparison: string; guide: string; fleetServer: string; fleetServerAddFleetServer: string; diff --git a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts index 3ca3a8505338bd..8cf49440f76b8b 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts @@ -55,8 +55,7 @@ const { startES } = kbnTestServer.createTestServers({ }); let esServer: kbnTestServer.TestElasticsearchUtils; -// Failing: See https://github.com/elastic/kibana/issues/113697 -describe.skip('migration actions', () => { +describe('migration actions', () => { let client: ElasticsearchClient; beforeAll(async () => { @@ -1158,7 +1157,7 @@ describe.skip('migration actions', () => { it('resolves left wait_for_task_completion_timeout when the task does not complete within the timeout', async () => { const res = (await pickupUpdatedMappings( client, - 'existing_index_with_docs' + '.kibana_1' )()) as Either.Right; const task = waitForPickupUpdatedMappingsTask({ @@ -1539,7 +1538,8 @@ describe.skip('migration actions', () => { } `); }); - it('resolves left request_entity_too_large_exception when the payload is too large', async () => { + // TODO: unskip after https://github.com/elastic/kibana/issues/116111 + it.skip('resolves left request_entity_too_large_exception when the payload is too large', async () => { const newDocs = new Array(10000).fill({ _source: { title: diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts index 348cbe88cd8a7a..ebc632f059ced2 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts @@ -40,7 +40,7 @@ describe('migration v2 with corrupt saved object documents', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('collects corrupt saved object documents across batches', async () => { + it.skip('collects corrupt saved object documents across batches', async () => { const { startES } = kbnTestServer.createTestServers({ adjustTimeout: (t: number) => jest.setTimeout(t), settings: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts index 506f42cb2e402e..9fd8f3884c4ae7 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts @@ -41,7 +41,7 @@ describe('migration v2', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('migrates the documents to the highest version', async () => { + it.skip('migrates the documents to the highest version', async () => { const migratedIndex = `.kibana_${pkg.version}_001`; const { startES } = kbnTestServer.createTestServers({ adjustTimeout: (t: number) => jest.setTimeout(t), diff --git a/src/plugins/charts/common/index.ts b/src/plugins/charts/common/index.ts index 618466212f5bb7..d06dbc73e29b1e 100644 --- a/src/plugins/charts/common/index.ts +++ b/src/plugins/charts/common/index.ts @@ -7,6 +7,7 @@ */ export const COLOR_MAPPING_SETTING = 'visualization:colorMapping'; +export const LEGACY_TIME_AXIS = 'visualization:useLegacyTimeAxis'; export { CustomPaletteArguments, diff --git a/src/plugins/charts/server/plugin.ts b/src/plugins/charts/server/plugin.ts index 63b703e6b75384..c7559b525cd223 100644 --- a/src/plugins/charts/server/plugin.ts +++ b/src/plugins/charts/server/plugin.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { CoreSetup, Plugin } from 'kibana/server'; -import { COLOR_MAPPING_SETTING, palette, systemPalette } from '../common'; +import { COLOR_MAPPING_SETTING, LEGACY_TIME_AXIS, palette, systemPalette } from '../common'; import { ExpressionsServerSetup } from '../../expressions/server'; interface SetupDependencies { @@ -45,6 +45,21 @@ export class ChartsServerPlugin implements Plugin { category: ['visualization'], schema: schema.string(), }, + [LEGACY_TIME_AXIS]: { + name: i18n.translate('charts.advancedSettings.visualization.useLegacyTimeAxis.name', { + defaultMessage: 'Legacy chart time axis', + }), + value: false, + description: i18n.translate( + 'charts.advancedSettings.visualization.useLegacyTimeAxis.description', + { + defaultMessage: + 'Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB', + } + ), + category: ['visualization'], + schema: schema.boolean(), + }, }); return {}; diff --git a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx index effbf8ce980d73..8a46a16c1bf0cf 100644 --- a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx @@ -231,7 +231,7 @@ export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => { - {showViewModeToggle && ( )} - {timefield && ( = useLegacyTimeAxis + ? {} + : { strokeWidth: 0.1, stroke: isDarkMode ? 'white' : 'black' }; + const verticalAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + visible: false, + }, + tickLabel: { + fontSize: 11, + }, + }; + const xAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + stroke: isDarkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: isDarkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + tickLabel: { + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + }; + return (
@@ -195,15 +241,19 @@ export function DiscoverHistogram({ xAxisFormatter.convert(value)} + gridLine={gridLineStyle} + style={verticalAxisStyle} /> 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 007d3a99cb1dd8..d8d0215fd751f2 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -64,6 +64,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'text', _meta: { description: 'Non-default value of setting.' }, }, + 'visualization:useLegacyTimeAxis': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'visualization:regionmap:showWarnings': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, @@ -436,10 +440,6 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'labs:canvas:byValueEmbeddable': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'labs:canvas:useDataService': { 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 d35a05fe04780b..9dcd2038edb9d7 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -40,6 +40,7 @@ export interface UsageStats { 'visualize:enableLabs': boolean; 'visualization:heatmap:maxBuckets': number; 'visualization:colorMapping': string; + 'visualization:useLegacyTimeAxis': boolean; 'visualization:regionmap:showWarnings': boolean; 'visualization:tileMap:maxPrecision': number; 'csv:separator': string; @@ -120,7 +121,6 @@ export interface UsageStats { 'banners:textColor': string; 'banners:backgroundColor': string; 'labs:canvas:enable_ui': boolean; - 'labs:canvas:byValueEmbeddable': boolean; 'labs:canvas:useDataService': boolean; 'labs:presentation:timeToPresent': boolean; 'labs:dashboard:enable_ui': boolean; diff --git a/src/plugins/presentation_util/common/labs.ts b/src/plugins/presentation_util/common/labs.ts index 8eefbd6981280c..cb976e73b5edfe 100644 --- a/src/plugins/presentation_util/common/labs.ts +++ b/src/plugins/presentation_util/common/labs.ts @@ -11,9 +11,7 @@ import { i18n } from '@kbn/i18n'; export const LABS_PROJECT_PREFIX = 'labs:'; export const DEFER_BELOW_FOLD = `${LABS_PROJECT_PREFIX}dashboard:deferBelowFold` as const; export const DASHBOARD_CONTROLS = `${LABS_PROJECT_PREFIX}dashboard:dashboardControls` as const; -export const BY_VALUE_EMBEDDABLE = `${LABS_PROJECT_PREFIX}canvas:byValueEmbeddable` as const; - -export const projectIDs = [DEFER_BELOW_FOLD, DASHBOARD_CONTROLS, BY_VALUE_EMBEDDABLE] as const; +export const projectIDs = [DEFER_BELOW_FOLD, DASHBOARD_CONTROLS] as const; export const environmentNames = ['kibana', 'browser', 'session'] as const; export const solutionNames = ['canvas', 'dashboard', 'presentation'] as const; @@ -50,19 +48,6 @@ export const projects: { [ID in ProjectID]: ProjectConfig & { id: ID } } = { }), solutions: ['dashboard'], }, - [BY_VALUE_EMBEDDABLE]: { - id: BY_VALUE_EMBEDDABLE, - isActive: true, - isDisplayed: true, - environments: ['kibana', 'browser', 'session'], - name: i18n.translate('presentationUtil.labs.enableByValueEmbeddableName', { - defaultMessage: 'By-Value Embeddables', - }), - description: i18n.translate('presentationUtil.labs.enableByValueEmbeddableDescription', { - defaultMessage: 'Enables support for by-value embeddables in Canvas', - }), - solutions: ['canvas'], - }, }; export type ProjectID = typeof projectIDs[number]; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss index c70e317546d403..535570a51d777c 100644 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss +++ b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss @@ -1,31 +1,11 @@ .quickButtonGroup { - .euiButtonGroup__buttons { - border-radius: $euiBorderRadius; - - .quickButtonGroup__button { - background-color: $euiColorEmptyShade; - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-width: $euiBorderWidthThin !important; - border-style: solid !important; - border-color: $euiBorderColor !important; - } - } - - .quickButtonGroup__button:first-of-type { - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-top-left-radius: $euiBorderRadius !important; - border-bottom-left-radius: $euiBorderRadius !important; - } - } - - .quickButtonGroup__button:last-of-type { - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-top-right-radius: $euiBorderRadius !important; - border-bottom-right-radius: $euiBorderRadius !important; - } + .quickButtonGroup__button { + background-color: $euiColorEmptyShade; + @include kbnThemeStyle('v8') { + // sass-lint:disable-block no-important + border-width: $euiBorderWidthThin !important; + border-style: solid !important; + border-color: $euiBorderColor !important; } } } diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 138ce3f097ce96..251fed955788eb 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7110,6 +7110,12 @@ "description": "Non-default value of setting." } }, + "visualization:useLegacyTimeAxis": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "visualization:regionmap:showWarnings": { "type": "boolean", "_meta": { @@ -7671,12 +7677,6 @@ "description": "Non-default value of setting." } }, - "labs:canvas:byValueEmbeddable": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "labs:canvas:useDataService": { "type": "boolean", "_meta": { diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js index b4fe39c522de76..b177ef632e2107 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js @@ -19,6 +19,7 @@ import { createFieldFormatter } from '../../lib/create_field_formatter'; import { checkIfSeriesHaveSameFormatters } from '../../lib/check_if_series_have_same_formatters'; import { TimeSeries } from '../../../visualizations/views/timeseries'; import { MarkdownSimple } from '../../../../../../../../plugins/kibana_react/public'; +import { LEGACY_TIME_AXIS } from '../../../../../../../../plugins/charts/common'; import { replaceVars } from '../../lib/replace_vars'; import { getInterval } from '../../lib/get_interval'; import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter'; @@ -272,6 +273,7 @@ class TimeseriesVisualization extends Component { syncColors={syncColors} palettesService={palettesService} interval={interval} + useLegacyTimeAxis={getConfig(LEGACY_TIME_AXIS, false)} isLastBucketDropped={Boolean( model.drop_last_bucket || model.series.some((series) => series.series_drop_last_bucket) diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 2158283bb80d57..5e98b74c0caa5b 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -72,6 +72,7 @@ export const TimeSeries = ({ palettesService, interval, isLastBucketDropped, + useLegacyTimeAxis, }) => { // If the color isn't configured by the user, use the color mapping service // to assign a color from the Kibana palette. Colors will be shared across the @@ -138,6 +139,51 @@ export const TimeSeries = ({ }, [palettesService, series, syncColors] ); + + const darkMode = uiSettings.get('theme:darkMode'); + const gridLineStyle = !useLegacyTimeAxis + ? { + visible: showGrid, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + ...GRID_LINE_CONFIG, + visible: showGrid, + }; + const xAxisStyle = !useLegacyTimeAxis + ? { + tickLabel: { + visible: true, + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + axisTitle: { + visible: true, + padding: 0, + }, + } + : {}; + return ( ); @@ -357,4 +402,5 @@ TimeSeries.propTypes = { annotations: PropTypes.array, interval: PropTypes.number, isLastBucketDropped: PropTypes.bool, + useLegacyTimeAxis: PropTypes.bool, }; diff --git a/src/plugins/vis_types/xy/public/components/xy_axis.tsx b/src/plugins/vis_types/xy/public/components/xy_axis.tsx index 30e1dbbff673ee..b224639bdbff3a 100644 --- a/src/plugins/vis_types/xy/public/components/xy_axis.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_axis.tsx @@ -25,6 +25,7 @@ export const XYAxis: FC = ({ domain, style, integersOnly, + timeAxisLayerCount, }) => ( = ({ labelFormat={ticks?.labelFormatter} showOverlappingLabels={ticks?.showOverlappingLabels} showDuplicatedTicks={ticks?.showDuplicates} + timeAxisLayerCount={timeAxisLayerCount} /> ); diff --git a/src/plugins/vis_types/xy/public/config/get_axis.ts b/src/plugins/vis_types/xy/public/config/get_axis.ts index 0d6c67d064cf82..796636ef2cb61f 100644 --- a/src/plugins/vis_types/xy/public/config/get_axis.ts +++ b/src/plugins/vis_types/xy/public/config/get_axis.ts @@ -8,7 +8,13 @@ import { identity } from 'lodash'; -import { AxisSpec, TickFormatter, YDomainRange, ScaleType as ECScaleType } from '@elastic/charts'; +import { + AxisSpec, + TickFormatter, + YDomainRange, + ScaleType as ECScaleType, + Position, +} from '@elastic/charts'; import { LabelRotation } from '../../../../charts/public'; import { BUCKET_TYPES } from '../../../../data/public'; @@ -33,7 +39,9 @@ export function getAxis( { categoryLines, valueAxis }: Grid, { params, format, formatter, title: fallbackTitle = '', aggType }: Aspect, seriesParams: SeriesParam[], - isDateHistogram = false + isDateHistogram = false, + useMultiLayerTimeAxis = false, + darkMode = false ): AxisConfig { const isCategoryAxis = type === AxisType.Category; // Hide unassigned axis, not supported in elastic charts @@ -74,9 +82,10 @@ export function getAxis( ticks, grid, scale, - style: getAxisStyle(ticks, title, fallbackRotation), + style: getAxisStyle(useMultiLayerTimeAxis, darkMode, ticks, title, fallbackRotation), domain: getAxisDomain(scale, isCategoryAxis), integersOnly: aggType === 'count', + timeAxisLayerCount: useMultiLayerTimeAxis ? 3 : 0, }; } @@ -147,19 +156,52 @@ export function getScale( } function getAxisStyle( + isMultiLayerTimeAxis: boolean, + darkMode: boolean, ticks?: TickOptions, title?: string, rotationFallback: LabelRotation = LabelRotation.Vertical ): AxisSpec['style'] { - return { - axisTitle: { - visible: (title ?? '').trim().length > 0, - }, - tickLabel: { - visible: ticks?.show, - rotation: -(ticks?.rotation ?? rotationFallback), - }, - }; + return isMultiLayerTimeAxis + ? { + tickLabel: { + visible: Boolean(ticks?.show), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(ticks?.show), + }, + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + } + : { + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + tickLabel: { + visible: ticks?.show, + rotation: -(ticks?.rotation ?? rotationFallback), + }, + }; } function getAxisDomain( diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index d2a3b9ad2a1037..bd79b915be9172 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -29,7 +29,12 @@ import { getAxis } from './get_axis'; import { getAspects } from './get_aspects'; import { ChartType } from '../index'; -export function getConfig(table: Datatable, params: VisParams): VisConfig { +export function getConfig( + table: Datatable, + params: VisParams, + useLegacyTimeAxis = false, + darkMode = false +): VisConfig { const { thresholdLine, orderBucketsBySum, @@ -42,13 +47,6 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { fillOpacity, } = params; const aspects = getAspects(table.columns, params.dimensions); - const xAxis = getAxis( - params.categoryAxes[0], - params.grid, - aspects.x, - params.seriesParams, - params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM - ); const tooltip = getTooltip(aspects, params); const yAxes = params.valueAxes.map((a) => { // find the correct aspect for each value axis @@ -60,10 +58,28 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { params.seriesParams ); }); + + const rotation = getRotation(params.categoryAxes[0]); + + const isDateHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM; + const isHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM; const enableHistogramMode = - (params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM || - params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM) && + (isDateHistogram || isHistogram) && shouldEnableHistogramMode(params.seriesParams, aspects.y, yAxes); + + const useMultiLayerTimeAxis = + enableHistogramMode && isDateHistogram && !useLegacyTimeAxis && rotation === 0; + + const xAxis = getAxis( + params.categoryAxes[0], + params.grid, + aspects.x, + params.seriesParams, + isDateHistogram, + useMultiLayerTimeAxis, + darkMode + ); + const isTimeChart = (aspects.x.params as DateHistogramParams).date ?? false; return { @@ -83,7 +99,7 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { xAxis, yAxes, legend: getLegend(params), - rotation: getRotation(params.categoryAxes[0]), + rotation, thresholdLine: getThresholdLine(thresholdLine, yAxes, params.seriesParams), }; } diff --git a/src/plugins/vis_types/xy/public/plugin.ts b/src/plugins/vis_types/xy/public/plugin.ts index c79ead242e35b0..0f1de387161e30 100644 --- a/src/plugins/vis_types/xy/public/plugin.ts +++ b/src/plugins/vis_types/xy/public/plugin.ts @@ -24,7 +24,7 @@ import { } from './services'; import { visTypesDefinitions } from './vis_types'; -import { xyVisRenderer } from './vis_renderer'; +import { getXYVisRenderer } from './vis_renderer'; import * as expressionFunctions from './expression_functions'; @@ -69,7 +69,11 @@ export class VisTypeXyPlugin setThemeService(charts.theme); setPalettesService(charts.palettes); - expressions.registerRenderer(xyVisRenderer); + expressions.registerRenderer( + getXYVisRenderer({ + uiSettings: core.uiSettings, + }) + ); expressions.registerFunction(expressionFunctions.visTypeXyVisFn); expressions.registerFunction(expressionFunctions.categoryAxis); expressions.registerFunction(expressionFunctions.timeMarker); diff --git a/src/plugins/vis_types/xy/public/types/config.ts b/src/plugins/vis_types/xy/public/types/config.ts index e52b47366bc857..287787193bd202 100644 --- a/src/plugins/vis_types/xy/public/types/config.ts +++ b/src/plugins/vis_types/xy/public/types/config.ts @@ -85,6 +85,7 @@ export interface AxisConfig { title?: string; grid?: AxisGrid; integersOnly: boolean; + timeAxisLayerCount?: number; } export interface LegendOptions { diff --git a/src/plugins/vis_types/xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx index 515ad3e7eaf6fa..8574e86a230962 100644 --- a/src/plugins/vis_types/xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -66,6 +66,7 @@ export interface VisComponentProps { fireEvent: IInterpreterRenderHandlers['event']; renderComplete: IInterpreterRenderHandlers['done']; syncColors: boolean; + useLegacyTimeAxis: boolean; } export type VisComponentType = typeof VisComponent; @@ -211,8 +212,9 @@ const VisComponent = (props: VisComponentProps) => { ); const { visData, visParams, syncColors } = props; + const isDarkMode = getThemeService().useDarkMode(); - const config = getConfig(visData, visParams); + const config = getConfig(visData, visParams, props.useLegacyTimeAxis, isDarkMode); const timeZone = getTimeZone(); const xDomain = config.xAxis.scale.type === ScaleType.Ordinal ? undefined : getXDomain(config.aspects.x.params); @@ -229,7 +231,7 @@ const VisComponent = (props: VisComponentProps) => { () => config.legend.position ?? Position.Right, [config.legend.position] ); - const isDarkMode = getThemeService().useDarkMode(); + const getSeriesName = getSeriesNameFn(config.aspects, config.aspects.y.length > 1); const splitAccessors = config.aspects.series?.map(({ accessor, formatter }) => { diff --git a/src/plugins/vis_types/xy/public/vis_renderer.tsx b/src/plugins/vis_types/xy/public/vis_renderer.tsx index 093671307d5383..77727761015a74 100644 --- a/src/plugins/vis_types/xy/public/vis_renderer.tsx +++ b/src/plugins/vis_types/xy/public/vis_renderer.tsx @@ -9,6 +9,7 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; +import { IUiSettingsClient } from 'kibana/public'; import { VisualizationContainer } from '../../../visualizations/public'; import type { PersistedState } from '../../../visualizations/public'; @@ -17,6 +18,7 @@ import type { ExpressionRenderDefinition } from '../../../expressions/public'; import type { XyVisType } from '../common'; import type { VisComponentType } from './vis_component'; import { RenderValue, visName } from './expression_functions/xy_vis_fn'; +import { LEGACY_TIME_AXIS } from '../../../charts/common'; // @ts-ignore const VisComponent = lazy(() => import('./vis_component')); @@ -28,7 +30,9 @@ function shouldShowNoResultsMessage(visData: any, visType: XyVisType): boolean { return Boolean(isZeroHits); } -export const xyVisRenderer: ExpressionRenderDefinition = { +export const getXYVisRenderer: (deps: { + uiSettings: IUiSettingsClient; +}) => ExpressionRenderDefinition = ({ uiSettings }) => ({ name: visName, displayName: 'XY visualization', reuseDomNode: true, @@ -46,10 +50,11 @@ export const xyVisRenderer: ExpressionRenderDefinition = { fireEvent={handlers.event} uiState={handlers.uiState as PersistedState} syncColors={syncColors} + useLegacyTimeAxis={uiSettings.get(LEGACY_TIME_AXIS, false)} /> , domNode ); }, -}; +}); diff --git a/test/common/services/bsearch.ts b/test/common/services/bsearch.ts new file mode 100644 index 00000000000000..d9fe89d9e4b9c7 --- /dev/null +++ b/test/common/services/bsearch.ts @@ -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 expect from '@kbn/expect'; +import request from 'superagent'; +import type SuperTest from 'supertest'; +import { IEsSearchResponse } from 'src/plugins/data/common'; +import { FtrProviderContext } from '../ftr_provider_context'; +import { RetryService } from './retry/retry'; + +/** + * Function copied from here: + * test/api_integration/apis/search/bsearch.ts without the compress + * + * Splits the JSON lines from bsearch + */ +const parseBfetchResponse = (resp: request.Response): Array> => { + return resp.text + .trim() + .split('\n') + .map((item) => JSON.parse(item)); +}; + +/** + * Function copied from here: + * x-pack/test/rule_registry/common/lib/authentication/spaces.ts + * @param spaceId The space id we want to utilize + */ +const getSpaceUrlPrefix = (spaceId?: string): string => { + return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``; +}; + +/** + * Options for the send method + */ +interface SendOptions { + supertest: SuperTest.SuperTest; + options: object; + strategy: string; + space?: string; +} + +/** + * Bsearch factory which will return a new bsearch capable service that can reduce flake + * on the CI systems when they are under pressure and bsearch returns an async search + * response or a sync response. + * + * @example + * const supertest = getService('supertest'); + * const bsearch = getService('bsearch'); + * const response = await bsearch.send({ + * supertest, + * options: { + * defaultIndex: ['large_volume_dns_data'], + * } + * strategy: 'securitySolutionSearchStrategy', + * }); + * expect(response).eql({ ... your value ... }); + */ +export const BSearchFactory = (retry: RetryService) => ({ + /** Send method to send in your supertest, url, options, and strategy name */ + send: async ({ + supertest, + options, + strategy, + space, + }: SendOptions): Promise => { + const spaceUrl = getSpaceUrlPrefix(space); + const { body } = await retry.try(async () => { + return supertest + .post(`${spaceUrl}/internal/search/${strategy}`) + .set('kbn-xsrf', 'true') + .send(options) + .expect(200); + }); + + if (body.isRunning) { + const result = await retry.try(async () => { + const resp = await supertest + .post(`${spaceUrl}/internal/bsearch`) + .set('kbn-xsrf', 'true') + .send({ + batch: [ + { + request: { + id: body.id, + ...options, + }, + options: { + strategy, + }, + }, + ], + }) + .expect(200); + const [parsedResponse] = parseBfetchResponse(resp); + expect(parsedResponse.result.isRunning).equal(false); + return parsedResponse.result; + }); + return result; + } else { + return body; + } + }, +}); + +/** + * Bsearch provider which will return a new bsearch capable service that can reduce flake + * on the CI systems when they are under pressure and bsearch returns an async search response + * or a sync response. + */ +export function BSearchProvider({ + getService, +}: FtrProviderContext): ReturnType { + const retry = getService('retry'); + return BSearchFactory(retry); +} diff --git a/test/common/services/index.ts b/test/common/services/index.ts index c04bd778468a9e..91d17ce1bb3e89 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -16,6 +16,7 @@ import { SecurityServiceProvider } from './security'; import { EsDeleteAllIndicesProvider } from './es_delete_all_indices'; import { SavedObjectInfoService } from './saved_object_info'; import { IndexPatternsService } from './index_patterns'; +import { BSearchProvider } from './bsearch'; export const services = { deployment: DeploymentService, @@ -28,4 +29,5 @@ export const services = { esDeleteAllIndices: EsDeleteAllIndicesProvider, savedObjectInfo: SavedObjectInfoService, indexPatterns: IndexPatternsService, + bsearch: BSearchProvider, }; diff --git a/test/examples/embeddables/dashboard.ts b/test/examples/embeddables/dashboard.ts index b97905ca9ce6a6..5c255b136c666d 100644 --- a/test/examples/embeddables/dashboard.ts +++ b/test/examples/embeddables/dashboard.ts @@ -100,7 +100,8 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide const PageObjects = getPageObjects(['common', 'visChart']); const monacoEditor = getService('monacoEditor'); - describe('dashboard container', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116414 + describe.skip('dashboard container', () => { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/dashboard/current/data'); await esArchiver.loadIfNeeded( diff --git a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts index 5744c8e64f5c11..fa4308ae72883a 100644 --- a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts +++ b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts @@ -12,7 +12,8 @@ import { PluginFunctionalProviderContext } from 'test/plugin_functional/services export default function ({ getService }: PluginFunctionalProviderContext) { const testSubjects = getService('testSubjects'); - describe('', () => { + // FAILING: https://github.com/elastic/kibana/issues/116463 + describe.skip('', () => { it('finds an index pattern', async () => { await testSubjects.existOrFail('indexPatternTitle'); }); diff --git a/test/functional/apps/dashboard/dashboard_snapshots.ts b/test/functional/apps/dashboard/dashboard_snapshots.ts index 3aba671c0a4b29..9279bbd5806e7b 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.ts +++ b/test/functional/apps/dashboard/dashboard_snapshots.ts @@ -59,7 +59,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); it('compare area chart snapshot', async () => { @@ -81,7 +81,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); }); } diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index c531ada8a25737..631d2148d73c39 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -167,7 +167,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); expect(secondAreaChartData).to.eql(secondAreaExpectedChartData); expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData); - expect(firstAxesLabels).to.eql(['12.19GB', '12.2GB', '12.21GB']); + expect(firstAxesLabels).to.eql(['12.2GB', '12.21GB']); expect(secondAxesLabels).to.eql(['5.59KB', '5.6KB']); expect(thirdAxesLabels.toString()).to.be( 'BYTES_5721,BYTES_5722,BYTES_5723,BYTES_5724,BYTES_5725,BYTES_5726,BYTES_5727,BYTES_5728,BYTES_5729,BYTES_5730,BYTES_5731,BYTES_5732,BYTES_5733' @@ -257,8 +257,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(value).to.eql('.es()'); }); - // FLAKY: https://github.com/elastic/kibana/issues/116033 - describe.skip('dynamic suggestions for argument values', () => { + describe('dynamic suggestions for argument values', () => { describe('.es()', () => { it('should show index pattern suggestions for index argument', async () => { await monacoEditor.setCodeEditorValue(''); diff --git a/test/functional/config.js b/test/functional/config.js index 5b0b79e84e8df3..09eccc863a0e53 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -54,6 +54,7 @@ export default async function ({ readConfigFile }) { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', 'visualization:visualize:legacyPieChartsLibrary': true, + 'visualization:useLegacyTimeAxis': true, }, }, diff --git a/test/functional/page_objects/timelion_page.ts b/test/functional/page_objects/timelion_page.ts index bdfde3c8145e5e..ba1db60bc6350d 100644 --- a/test/functional/page_objects/timelion_page.ts +++ b/test/functional/page_objects/timelion_page.ts @@ -7,13 +7,21 @@ */ import { FtrService } from '../ftr_provider_context'; +import type { WebElementWrapper } from '../services/lib/web_element_wrapper'; export class TimelionPageObject extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); + private readonly retry = this.ctx.getService('retry'); public async getSuggestionItemsText() { - const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); - const lists = await timelionCodeEditor.findAllByClassName('monaco-list-row'); + let lists: WebElementWrapper[] = []; + await this.retry.try(async () => { + const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); + lists = await timelionCodeEditor.findAllByClassName('monaco-list-row'); + if (lists.length === 0) { + throw new Error('suggestion list not populated'); + } + }); return await Promise.all(lists.map(async (element) => await element.getVisibleText())); } diff --git a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts index 8e7adb504ebee3..b384c3fbbbb1ec 100644 --- a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts +++ b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts @@ -68,7 +68,8 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide }); }); - describe('Delete modal', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116059 + describe.skip('Delete modal', () => { it('should display a warning then trying to delete hidden saved objects', async () => { await PageObjects.savedObjects.clickCheckboxByTitle('A Pie'); await PageObjects.savedObjects.clickCheckboxByTitle('A Dashboard'); diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 0f509fa8ba1327..050b62646fb3bc 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -137,6 +137,7 @@ def functionalXpack(Map params = [:]) { 'x-pack/plugins/security_solution/', 'x-pack/plugins/cases/', 'x-pack/plugins/timelines/', + 'x-pack/plugins/lists/', 'x-pack/test/security_solution_cypress/', 'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/', 'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx', diff --git a/x-pack/package.json b/x-pack/package.json index 805d8555bf453f..8fb7a3483e5ef0 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -1,6 +1,6 @@ { "name": "x-pack", - "version": "8.0.0", + "version": "8.1.0", "author": "Elastic", "private": true, "license": "Elastic-License", diff --git a/x-pack/plugins/apm/ftr_e2e/README.md b/x-pack/plugins/apm/ftr_e2e/README.md index 2df4e837d2e555..96d6671bb36990 100644 --- a/x-pack/plugins/apm/ftr_e2e/README.md +++ b/x-pack/plugins/apm/ftr_e2e/README.md @@ -4,4 +4,4 @@ APM uses [FTR](../../../../packages/kbn-test/README.md) (functional test runner) ## Running tests -Go to [tests documentation](../scripts/test#e2e-tests-cypress/README.md) \ No newline at end of file +Go to [tests documentation](../dev_docs/testing.md#e2e-tests-cypress) diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts new file mode 100644 index 00000000000000..0ab2d5682a900c --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts @@ -0,0 +1,80 @@ +/* + * 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. + */ + +const timeRange = { + rangeFrom: Cypress.env('START_DATE'), + rangeTo: Cypress.env('END_DATE'), +}; + +describe('Dependencies', () => { + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); + + describe('top-level dependencies page', () => { + it('has a list of dependencies and you can navigate to the page for one', () => { + cy.visit(`/app/apm/services?${new URLSearchParams(timeRange)}`); + cy.contains('nav a', 'Dependencies').click(); + + // `force: true` because Cypress says the element is 0x0 + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); + + describe('dependency overview page', () => { + it('shows dependency information and you can navigate to a page for an upstream service', () => { + cy.visit( + `/app/apm/backends/overview?${new URLSearchParams({ + ...timeRange, + backendName: 'postgresql', + })}` + ); + + cy.get('[data-test-subj="latencyChart"]'); + cy.get('[data-test-subj="throughputChart"]'); + cy.get('[data-test-subj="errorRateChart"]'); + + cy.contains('opbeans-python').click({ force: true }); + + cy.contains('h1', 'opbeans-python'); + }); + }); + + describe('service overview page', () => { + it('shows dependency information and you can navigate to a page for a dependency', () => { + cy.visit( + `/app/apm/services/opbeans-python/overview?${new URLSearchParams( + timeRange + )}` + ); + + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); + + describe('service dependencies tab', () => { + it('shows dependency information and you can navigate to a page for a dependency', () => { + cy.visit( + `/app/apm/services/opbeans-python/overview?${new URLSearchParams( + timeRange + )}` + ); + + cy.contains('a[role="tab"]', 'Dependencies').click(); + + cy.contains('Time spent by dependency'); + + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); +}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts index f1ede936c6ace4..ac2e8e8babee1e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts @@ -6,11 +6,12 @@ */ import { ExpressionTypeDefinition } from '../../../../../src/plugins/expressions'; -import { EmbeddableInput } from '../../types'; +import { EmbeddableInput } from '../../../../../src/plugins/embeddable/common/'; import { EmbeddableTypes } from './embeddable_types'; export const EmbeddableExpressionType = 'embeddable'; export { EmbeddableTypes, EmbeddableInput }; + export interface EmbeddableExpression { /** * The type of the expression result diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts index d6d7a0f867849c..2cfdebafb70df4 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts @@ -6,6 +6,7 @@ */ import { functions as commonFunctions } from '../common'; +import { functions as externalFunctions } from '../external'; import { location } from './location'; import { markdown } from './markdown'; import { urlparam } from './urlparam'; @@ -13,4 +14,13 @@ import { escount } from './escount'; import { esdocs } from './esdocs'; import { essql } from './essql'; -export const functions = [location, markdown, urlparam, escount, esdocs, essql, ...commonFunctions]; +export const functions = [ + location, + markdown, + urlparam, + escount, + esdocs, + essql, + ...commonFunctions, + ...externalFunctions, +]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts deleted file mode 100644 index 001fb0e3f62e3a..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts +++ /dev/null @@ -1,60 +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 { embeddableFunctionFactory } from './embeddable'; -import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { ExpressionValueFilter } from '../../../types'; -import { encode } from '../../../common/lib/embeddable_dataurl'; -import { InitializeArguments } from '.'; - -const filterContext: ExpressionValueFilter = { - type: 'filter', - and: [ - { - type: 'filter', - and: [], - value: 'filter-value', - column: 'filter-column', - filterType: 'exactly', - }, - { - type: 'filter', - and: [], - column: 'time-column', - filterType: 'time', - from: '2019-06-04T04:00:00.000Z', - to: '2019-06-05T04:00:00.000Z', - }, - ], -}; - -describe('embeddable', () => { - const fn = embeddableFunctionFactory({} as InitializeArguments)().fn; - const config = { - id: 'some-id', - timerange: { from: '15m', to: 'now' }, - title: 'test embeddable', - }; - - const args = { - config: encode(config), - type: 'visualization', - }; - - it('accepts null context', () => { - const expression = fn(null, args, {} as any); - - expect(expression.input.filters).toEqual([]); - }); - - it('accepts filter context', () => { - const expression = fn(filterContext, args, {} as any); - const embeddableFilters = getQueryFilters(filterContext.and); - - expect(expression.input.filters).toEqual(embeddableFilters); - }); -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts deleted file mode 100644 index 7ef8f0a09eb907..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts +++ /dev/null @@ -1,145 +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 { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { ExpressionValueFilter, EmbeddableInput } from '../../../types'; -import { EmbeddableExpressionType, EmbeddableExpression } from '../../expression_types'; -import { getFunctionHelp } from '../../../i18n'; -import { SavedObjectReference } from '../../../../../../src/core/types'; -import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { decode, encode } from '../../../common/lib/embeddable_dataurl'; -import { InitializeArguments } from '.'; - -export interface Arguments { - config: string; - type: string; -} - -const defaultTimeRange = { - from: 'now-15m', - to: 'now', -}; - -const baseEmbeddableInput = { - timeRange: defaultTimeRange, - disableTriggers: true, - renderMode: 'noInteractivity', -}; - -type Return = EmbeddableExpression; - -type EmbeddableFunction = ExpressionFunctionDefinition< - 'embeddable', - ExpressionValueFilter | null, - Arguments, - Return ->; - -export function embeddableFunctionFactory({ - embeddablePersistableStateService, -}: InitializeArguments): () => EmbeddableFunction { - return function embeddable(): EmbeddableFunction { - const { help, args: argHelp } = getFunctionHelp().embeddable; - - return { - name: 'embeddable', - help, - args: { - config: { - aliases: ['_'], - types: ['string'], - required: true, - help: argHelp.config, - }, - type: { - types: ['string'], - required: true, - help: argHelp.type, - }, - }, - context: { - types: ['filter'], - }, - type: EmbeddableExpressionType, - fn: (input, args) => { - const filters = input ? input.and : []; - - const embeddableInput = decode(args.config) as EmbeddableInput; - - return { - type: EmbeddableExpressionType, - input: { - ...baseEmbeddableInput, - ...embeddableInput, - filters: getQueryFilters(filters), - }, - generatedAt: Date.now(), - embeddableType: args.type, - }; - }, - - extract(state) { - const input = decode(state.config[0] as string); - - // extracts references for by-reference embeddables - if (input.savedObjectId) { - const refName = 'embeddable.savedObjectId'; - - const references: SavedObjectReference[] = [ - { - name: refName, - type: state.type[0] as string, - id: input.savedObjectId as string, - }, - ]; - - return { - state, - references, - }; - } - - // extracts references for by-value embeddables - const { state: extractedState, references: extractedReferences } = - embeddablePersistableStateService.extract({ - ...input, - type: state.type[0], - }); - - const { type, ...extractedInput } = extractedState; - - return { - state: { ...state, config: [encode(extractedInput)], type: [type] }, - references: extractedReferences, - }; - }, - - inject(state, references) { - const input = decode(state.config[0] as string); - const savedObjectReference = references.find( - (ref) => ref.name === 'embeddable.savedObjectId' - ); - - // injects saved object id for by-references embeddable - if (savedObjectReference) { - input.savedObjectId = savedObjectReference.id; - state.config[0] = encode(input); - state.type[0] = savedObjectReference.type; - } else { - // injects references for by-value embeddables - const { type, ...injectedInput } = embeddablePersistableStateService.inject( - { ...input, type: state.type[0] }, - references - ); - state.config[0] = encode(injectedInput); - state.type[0] = type; - } - return state; - }, - }; - }; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts index 1d69e181b5fd9d..407a0e2ebfe05a 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts @@ -5,26 +5,9 @@ * 2.0. */ -import { EmbeddableStart } from 'src/plugins/embeddable/public'; -import { embeddableFunctionFactory } from './embeddable'; import { savedLens } from './saved_lens'; import { savedMap } from './saved_map'; import { savedSearch } from './saved_search'; import { savedVisualization } from './saved_visualization'; -export interface InitializeArguments { - embeddablePersistableStateService: { - extract: EmbeddableStart['extract']; - inject: EmbeddableStart['inject']; - }; -} - -export function initFunctions(initialize: InitializeArguments) { - return [ - embeddableFunctionFactory(initialize), - savedLens, - savedMap, - savedSearch, - savedVisualization, - ]; -} +export const functions = [savedLens, savedMap, savedVisualization, savedSearch]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts index 67947691f7757c..082a69a874cae2 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts @@ -9,8 +9,9 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { PaletteOutput } from 'src/plugins/charts/common'; import { Filter as DataFilter } from '@kbn/es-query'; import { TimeRange } from 'src/plugins/data/common'; +import { EmbeddableInput } from 'src/plugins/embeddable/common'; import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { ExpressionValueFilter, EmbeddableInput, TimeRange as TimeRangeArg } from '../../../types'; +import { ExpressionValueFilter, TimeRange as TimeRangeArg } from '../../../types'; import { EmbeddableTypes, EmbeddableExpressionType, @@ -26,7 +27,7 @@ interface Arguments { } export type SavedLensInput = EmbeddableInput & { - savedObjectId: string; + id: string; timeRange?: TimeRange; filters: DataFilter[]; palette?: PaletteOutput; @@ -72,19 +73,18 @@ export function savedLens(): ExpressionFunctionDefinition< }, }, type: EmbeddableExpressionType, - fn: (input, { id, timerange, title, palette }) => { + fn: (input, args) => { const filters = input ? input.and : []; return { type: EmbeddableExpressionType, input: { - id, - savedObjectId: id, + id: args.id, filters: getQueryFilters(filters), - timeRange: timerange || defaultTimeRange, - title: title === null ? undefined : title, + timeRange: args.timerange || defaultTimeRange, + title: args.title === null ? undefined : args.title, disableTriggers: true, - palette, + palette: args.palette, }, embeddableType: EmbeddableTypes.lens, generatedAt: Date.now(), diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts index a7471c755155c8..538ed3f9198239 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts @@ -30,7 +30,7 @@ const defaultTimeRange = { to: 'now', }; -type Output = EmbeddableExpression; +type Output = EmbeddableExpression; export function savedMap(): ExpressionFunctionDefinition< 'savedMap', @@ -85,9 +85,8 @@ export function savedMap(): ExpressionFunctionDefinition< return { type: EmbeddableExpressionType, input: { - id: args.id, attributes: { title: '' }, - savedObjectId: args.id, + id: args.id, filters: getQueryFilters(filters), timeRange: args.timerange || defaultTimeRange, refreshConfig: { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts index 31e3fb2a8c5643..5c0442b43250c8 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts @@ -25,7 +25,7 @@ interface Arguments { title: string | null; } -type Output = EmbeddableExpression; +type Output = EmbeddableExpression; const defaultTimeRange = { from: 'now-15m', @@ -94,7 +94,6 @@ export function savedVisualization(): ExpressionFunctionDefinition< type: EmbeddableExpressionType, input: { id, - savedObjectId: id, disableTriggers: true, timeRange: timerange || defaultTimeRange, filters: getQueryFilters(filters), diff --git a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts index 591795637aebea..91c573fc4148ba 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts @@ -7,14 +7,12 @@ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { ChartsPluginStart } from 'src/plugins/charts/public'; -import { PresentationUtilPluginStart } from 'src/plugins/presentation_util/public'; import { CanvasSetup } from '../public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; import { functions } from './functions/browser'; -import { initFunctions } from './functions/external'; import { typeFunctions } from './expression_types'; import { renderFunctions, renderFunctionFactories } from './renderers'; @@ -27,7 +25,6 @@ export interface StartDeps { uiActions: UiActionsStart; inspector: InspectorStart; charts: ChartsPluginStart; - presentationUtil: PresentationUtilPluginStart; } export type SetupInitializer = (core: CoreSetup, plugins: SetupDeps) => T; @@ -42,13 +39,6 @@ export class CanvasSrcPlugin implements Plugin plugins.canvas.addRenderers(renderFunctions); core.getStartServices().then(([coreStart, depsStart]) => { - const externalFunctions = initFunctions({ - embeddablePersistableStateService: { - extract: depsStart.embeddable.extract, - inject: depsStart.embeddable.inject, - }, - }); - plugins.canvas.addFunctions(externalFunctions); plugins.canvas.addRenderers( renderFunctionFactories.map((factory: any) => factory(coreStart, depsStart)) ); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index 953746c2808406..73e839433c25e0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -13,17 +13,16 @@ import { IEmbeddable, EmbeddableFactory, EmbeddableFactoryNotFoundError, - isErrorEmbeddable, } from '../../../../../../src/plugins/embeddable/public'; import { EmbeddableExpression } from '../../expression_types/embeddable'; import { RendererStrings } from '../../../i18n'; import { embeddableInputToExpression } from './embeddable_input_to_expression'; -import { RendererFactory, EmbeddableInput } from '../../../types'; +import { EmbeddableInput } from '../../expression_types'; +import { RendererFactory } from '../../../types'; import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib'; const { embeddable: strings } = RendererStrings; -// registry of references to embeddables on the workpad const embeddablesRegistry: { [key: string]: IEmbeddable | Promise; } = {}; @@ -31,11 +30,11 @@ const embeddablesRegistry: { const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => { const I18nContext = core.i18n.Context; - return (embeddableObject: IEmbeddable) => { + return (embeddableObject: IEmbeddable, domNode: HTMLElement) => { return (
@@ -57,9 +56,6 @@ export const embeddableRendererFactory = ( reuseDomNode: true, render: async (domNode, { input, embeddableType }, handlers) => { const uniqueId = handlers.getElementId(); - const isByValueEnabled = plugins.presentationUtil.labsService.isProjectEnabled( - 'labs:canvas:byValueEmbeddable' - ); if (!embeddablesRegistry[uniqueId]) { const factory = Array.from(plugins.embeddable.getEmbeddableFactories()).find( @@ -71,27 +67,15 @@ export const embeddableRendererFactory = ( throw new EmbeddableFactoryNotFoundError(embeddableType); } - const embeddableInput = { ...input, id: uniqueId }; - - const embeddablePromise = input.savedObjectId - ? factory - .createFromSavedObject(input.savedObjectId, embeddableInput) - .then((embeddable) => { - // stores embeddable in registrey - embeddablesRegistry[uniqueId] = embeddable; - return embeddable; - }) - : factory.create(embeddableInput).then((embeddable) => { - if (!embeddable || isErrorEmbeddable(embeddable)) { - return; - } - // stores embeddable in registry - embeddablesRegistry[uniqueId] = embeddable as IEmbeddable; - return embeddable; - }); - embeddablesRegistry[uniqueId] = embeddablePromise as Promise; - - const embeddableObject = (await (async () => embeddablePromise)()) as IEmbeddable; + const embeddablePromise = factory + .createFromSavedObject(input.id, input) + .then((embeddable) => { + embeddablesRegistry[uniqueId] = embeddable; + return embeddable; + }); + embeddablesRegistry[uniqueId] = embeddablePromise; + + const embeddableObject = await (async () => embeddablePromise)(); const palettes = await plugins.charts.palettes.getPalettes(); @@ -102,8 +86,7 @@ export const embeddableRendererFactory = ( const updatedExpression = embeddableInputToExpression( updatedInput, embeddableType, - palettes, - isByValueEnabled + palettes ); if (updatedExpression) { @@ -111,7 +94,15 @@ export const embeddableRendererFactory = ( } }); - ReactDOM.render(renderEmbeddable(embeddableObject), domNode, () => handlers.done()); + ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () => + handlers.done() + ); + + handlers.onResize(() => { + ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () => + handlers.done() + ); + }); handlers.onDestroy(() => { subscription.unsubscribe(); @@ -124,7 +115,6 @@ export const embeddableRendererFactory = ( } else { const embeddable = embeddablesRegistry[uniqueId]; - // updating embeddable input with changes made to expression or filters if ('updateInput' in embeddable) { embeddable.updateInput(input); embeddable.reload(); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts index 80830eac240212..41cefad6a470fa 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts @@ -10,7 +10,6 @@ import { EmbeddableTypes, EmbeddableInput } from '../../expression_types'; import { toExpression as mapToExpression } from './input_type_to_expression/map'; import { toExpression as visualizationToExpression } from './input_type_to_expression/visualization'; import { toExpression as lensToExpression } from './input_type_to_expression/lens'; -import { toExpression as genericToExpression } from './input_type_to_expression/embeddable'; export const inputToExpressionTypeMap = { [EmbeddableTypes.map]: mapToExpression, @@ -24,13 +23,8 @@ export const inputToExpressionTypeMap = { export function embeddableInputToExpression( input: EmbeddableInput, embeddableType: string, - palettes: PaletteRegistry, - useGenericEmbeddable?: boolean + palettes: PaletteRegistry ): string | undefined { - if (useGenericEmbeddable) { - return genericToExpression(input, embeddableType); - } - if (inputToExpressionTypeMap[embeddableType]) { return inputToExpressionTypeMap[embeddableType](input as any, palettes); } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts deleted file mode 100644 index 4b78acec8750ac..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts +++ /dev/null @@ -1,128 +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 { toExpression } from './embeddable'; -import { EmbeddableInput } from '../../../../types'; -import { decode } from '../../../../common/lib/embeddable_dataurl'; -import { fromExpression } from '@kbn/interpreter/common'; - -describe('toExpression', () => { - describe('by-reference embeddable input', () => { - const baseEmbeddableInput = { - id: 'elementId', - savedObjectId: 'embeddableId', - filters: [], - }; - - it('converts to an embeddable expression', () => { - const input: EmbeddableInput = baseEmbeddableInput; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - expect(ast.type).toBe('expression'); - expect(ast.chain[0].function).toBe('embeddable'); - expect(ast.chain[0].arguments.type[0]).toBe('visualization'); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config.savedObjectId).toStrictEqual(input.savedObjectId); - }); - - it('includes optional input values', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: 'title', - timeRange: { - from: 'now-1h', - to: 'now', - }, - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - expect(config).toHaveProperty('timeRange'); - expect(config.timeRange).toHaveProperty('from', input.timeRange?.from); - expect(config.timeRange).toHaveProperty('to', input.timeRange?.to); - }); - - it('includes empty panel title', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: '', - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - }); - }); - - describe('by-value embeddable input', () => { - const baseEmbeddableInput = { - id: 'elementId', - disableTriggers: true, - filters: [], - }; - it('converts to an embeddable expression', () => { - const input: EmbeddableInput = baseEmbeddableInput; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - expect(ast.type).toBe('expression'); - expect(ast.chain[0].function).toBe('embeddable'); - expect(ast.chain[0].arguments.type[0]).toBe('visualization'); - - const config = decode(ast.chain[0].arguments.config[0] as string); - expect(config.filters).toStrictEqual(input.filters); - expect(config.disableTriggers).toStrictEqual(input.disableTriggers); - }); - - it('includes optional input values', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: 'title', - timeRange: { - from: 'now-1h', - to: 'now', - }, - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - expect(config).toHaveProperty('timeRange'); - expect(config.timeRange).toHaveProperty('from', input.timeRange?.from); - expect(config.timeRange).toHaveProperty('to', input.timeRange?.to); - }); - - it('includes empty panel title', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: '', - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - }); - }); -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts deleted file mode 100644 index 94d86f6640be1d..00000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts +++ /dev/null @@ -1,13 +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 { encode } from '../../../../common/lib/embeddable_dataurl'; -import { EmbeddableInput } from '../../../expression_types'; - -export function toExpression(input: EmbeddableInput, embeddableType: string): string { - return `embeddable config="${encode(input)}" type="${embeddableType}"`; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts index 224cdfba389d77..24da7238bcee94 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts @@ -11,8 +11,7 @@ import { fromExpression, Ast } from '@kbn/interpreter/common'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; const baseEmbeddableInput = { - id: 'elementId', - savedObjectId: 'embeddableId', + id: 'embeddableId', filters: [], }; @@ -28,7 +27,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedLens'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); expect(ast.chain[0].arguments).not.toHaveProperty('title'); expect(ast.chain[0].arguments).not.toHaveProperty('timerange'); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts index 5a13b73b3fe746..35e106f234fa4e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts @@ -14,7 +14,7 @@ export function toExpression(input: SavedLensInput, palettes: PaletteRegistry): expressionParts.push('savedLens'); - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts index af7b40a9b283d9..804d0d849cc7f7 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts @@ -6,12 +6,12 @@ */ import { toExpression } from './map'; +import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable'; import { fromExpression, Ast } from '@kbn/interpreter/common'; const baseSavedMapInput = { - id: 'elementId', attributes: { title: '' }, - savedObjectId: 'embeddableId', + id: 'embeddableId', filters: [], isLayerTOCOpen: false, refreshConfig: { @@ -23,7 +23,7 @@ const baseSavedMapInput = { describe('toExpression', () => { it('converts to a savedMap expression', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, }; @@ -33,7 +33,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedMap'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); expect(ast.chain[0].arguments).not.toHaveProperty('title'); expect(ast.chain[0].arguments).not.toHaveProperty('center'); @@ -41,7 +41,7 @@ describe('toExpression', () => { }); it('includes optional input values', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, mapCenter: { lat: 1, @@ -73,7 +73,7 @@ describe('toExpression', () => { }); it('includes empty panel title', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, title: '', }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts index 03746f38b4696c..3fd6a68a327c60 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts @@ -5,14 +5,13 @@ * 2.0. */ -import { MapEmbeddableInput } from '../../../../../../plugins/maps/public'; +import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable'; -export function toExpression(input: MapEmbeddableInput & { savedObjectId: string }): string { +export function toExpression(input: MapEmbeddableInput): string { const expressionParts = [] as string[]; expressionParts.push('savedMap'); - - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts index 4c61a130f3c95f..c5106b9a102b40 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts @@ -9,8 +9,7 @@ import { toExpression } from './visualization'; import { fromExpression, Ast } from '@kbn/interpreter/common'; const baseInput = { - id: 'elementId', - savedObjectId: 'embeddableId', + id: 'embeddableId', }; describe('toExpression', () => { @@ -25,7 +24,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedVisualization'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); }); it('includes timerange if given', () => { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts index 364d7cd0755db4..bcb73b2081fee5 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts @@ -7,11 +7,11 @@ import { VisualizeInput } from 'src/plugins/visualizations/public'; -export function toExpression(input: VisualizeInput & { savedObjectId: string }): string { +export function toExpression(input: VisualizeInput): string { const expressionParts = [] as string[]; expressionParts.push('savedVisualization'); - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts b/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts deleted file mode 100644 index e76dedfe63b14a..00000000000000 --- a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts +++ /dev/null @@ -1,13 +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 { EmbeddableInput } from '../../types'; - -export const encode = (input: Partial) => - Buffer.from(JSON.stringify(input)).toString('base64'); -export const decode = (serializedInput: string) => - JSON.parse(Buffer.from(serializedInput, 'base64').toString()); diff --git a/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts b/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts deleted file mode 100644 index 279f58799e8c00..00000000000000 --- a/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts +++ /dev/null @@ -1,25 +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 { i18n } from '@kbn/i18n'; -import { embeddableFunctionFactory } from '../../../canvas_plugin_src/functions/external/embeddable'; -import { FunctionHelp } from '../function_help'; -import { FunctionFactory } from '../../../types'; - -export const help: FunctionHelp>> = { - help: i18n.translate('xpack.canvas.functions.embeddableHelpText', { - defaultMessage: `Returns an embeddable with the provided configuration`, - }), - args: { - config: i18n.translate('xpack.canvas.functions.embeddable.args.idHelpText', { - defaultMessage: `The base64 encoded embeddable input object`, - }), - type: i18n.translate('xpack.canvas.functions.embeddable.args.typeHelpText', { - defaultMessage: `The embeddable type`, - }), - }, -}; diff --git a/x-pack/plugins/canvas/i18n/functions/function_help.ts b/x-pack/plugins/canvas/i18n/functions/function_help.ts index 520d32af1c272c..5eae785fefa2ea 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_help.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_help.ts @@ -27,7 +27,6 @@ import { help as demodata } from './dict/demodata'; import { help as doFn } from './dict/do'; import { help as dropdownControl } from './dict/dropdown_control'; import { help as eq } from './dict/eq'; -import { help as embeddable } from './dict/embeddable'; import { help as escount } from './dict/escount'; import { help as esdocs } from './dict/esdocs'; import { help as essql } from './dict/essql'; @@ -183,7 +182,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({ do: doFn, dropdownControl, eq, - embeddable, escount, esdocs, essql, diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 2fd312502a3c74..9c4d1b2179d821 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -25,7 +25,6 @@ "features", "inspector", "presentationUtil", - "visualizations", "uiActions", "share" ], diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx index 57f52fcf21f0f9..bf731876bf8c88 100644 --- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx +++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useCallback } from 'react'; +import React, { FC } from 'react'; import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,44 +27,38 @@ const strings = { }; export interface Props { onClose: () => void; - onSelect: (id: string, embeddableType: string, isByValueEnabled?: boolean) => void; + onSelect: (id: string, embeddableType: string) => void; availableEmbeddables: string[]; - isByValueEnabled?: boolean; } -export const AddEmbeddableFlyout: FC = ({ - onSelect, - availableEmbeddables, - onClose, - isByValueEnabled, -}) => { +export const AddEmbeddableFlyout: FC = ({ onSelect, availableEmbeddables, onClose }) => { const embeddablesService = useEmbeddablesService(); const platformService = usePlatformService(); const { getEmbeddableFactories } = embeddablesService; const { getSavedObjects, getUISettings } = platformService; - const onAddPanel = useCallback( - (id: string, savedObjectType: string) => { - const embeddableFactories = getEmbeddableFactories(); - // Find the embeddable type from the saved object type - const found = Array.from(embeddableFactories).find((embeddableFactory) => { - return Boolean( - embeddableFactory.savedObjectMetaData && - embeddableFactory.savedObjectMetaData.type === savedObjectType - ); - }); + const onAddPanel = (id: string, savedObjectType: string, name: string) => { + const embeddableFactories = getEmbeddableFactories(); - const foundEmbeddableType = found ? found.type : 'unknown'; + // Find the embeddable type from the saved object type + const found = Array.from(embeddableFactories).find((embeddableFactory) => { + return Boolean( + embeddableFactory.savedObjectMetaData && + embeddableFactory.savedObjectMetaData.type === savedObjectType + ); + }); - onSelect(id, foundEmbeddableType, isByValueEnabled); - }, - [isByValueEnabled, getEmbeddableFactories, onSelect] - ); + const foundEmbeddableType = found ? found.type : 'unknown'; + + onSelect(id, foundEmbeddableType); + }; const embeddableFactories = getEmbeddableFactories(); const availableSavedObjects = Array.from(embeddableFactories) - .filter((factory) => isByValueEnabled || availableEmbeddables.includes(factory.type)) + .filter((factory) => { + return availableEmbeddables.includes(factory.type); + }) .map((factory) => factory.savedObjectMetaData) .filter>(function ( maybeSavedObjectMetaData diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx index 4dc8d963932d8f..770a4cac606b0b 100644 --- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx +++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx @@ -8,14 +8,12 @@ import React, { useMemo, useEffect, useCallback } from 'react'; import { createPortal } from 'react-dom'; import { useSelector, useDispatch } from 'react-redux'; -import { encode } from '../../../common/lib/embeddable_dataurl'; import { AddEmbeddableFlyout as Component, Props as ComponentProps } from './flyout.component'; // @ts-expect-error untyped local import { addElement } from '../../state/actions/elements'; import { getSelectedPage } from '../../state/selectors/workpad'; import { EmbeddableTypes } from '../../../canvas_plugin_src/expression_types/embeddable'; import { State } from '../../../types'; -import { useLabsService } from '../../services'; const allowedEmbeddables = { [EmbeddableTypes.map]: (id: string) => { @@ -67,9 +65,6 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ availableEmbeddables, ...restProps }) => { - const labsService = useLabsService(); - const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable'); - const dispatch = useDispatch(); const pageId = useSelector((state) => getSelectedPage(state)); @@ -79,27 +74,18 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ ); const onSelect = useCallback( - (id: string, type: string): void => { + (id: string, type: string) => { const partialElement = { expression: `markdown "Could not find embeddable for type ${type}" | render`, }; - - // If by-value is enabled, we'll handle both by-reference and by-value embeddables - // with the new generic `embeddable` function. - // Otherwise we fallback to the embeddable type specific expressions. - if (isByValueEnabled) { - const config = encode({ savedObjectId: id }); - partialElement.expression = `embeddable config="${config}" - type="${type}" -| render`; - } else if (allowedEmbeddables[type]) { + if (allowedEmbeddables[type]) { partialElement.expression = allowedEmbeddables[type](id); } addEmbeddable(pageId, partialElement); restProps.onClose(); }, - [addEmbeddable, pageId, restProps, isByValueEnabled] + [addEmbeddable, pageId, restProps] ); return ( @@ -107,7 +93,6 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ {...restProps} availableEmbeddables={availableEmbeddables || []} onSelect={onSelect} - isByValueEnabled={isByValueEnabled} /> ); }; diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx index ffd5b095b12e52..50d527036560ad 100644 --- a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx +++ b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx @@ -6,5 +6,3 @@ */ export { useDownloadWorkpad, useDownloadRenderedWorkpad } from './use_download_workpad'; - -export { useIncomingEmbeddable } from './use_incoming_embeddable'; diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts b/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts deleted file mode 100644 index 2f8e2503ea57ef..00000000000000 --- a/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts +++ /dev/null @@ -1,86 +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 { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { fromExpression } from '@kbn/interpreter/common'; -import { CANVAS_APP } from '../../../../common/lib'; -import { decode, encode } from '../../../../common/lib/embeddable_dataurl'; -import { CanvasElement, CanvasPage } from '../../../../types'; -import { useEmbeddablesService, useLabsService } from '../../../services'; -// @ts-expect-error unconverted file -import { addElement } from '../../../state/actions/elements'; -// @ts-expect-error unconverted file -import { selectToplevelNodes } from '../../../state/actions/transient'; - -import { - updateEmbeddableExpression, - fetchEmbeddableRenderable, -} from '../../../state/actions/embeddable'; -import { clearValue } from '../../../state/actions/resolved_args'; - -export const useIncomingEmbeddable = (selectedPage: CanvasPage) => { - const embeddablesService = useEmbeddablesService(); - const labsService = useLabsService(); - const dispatch = useDispatch(); - const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable'); - const stateTransferService = embeddablesService.getStateTransfer(); - - // fetch incoming embeddable from state transfer service. - const incomingEmbeddable = stateTransferService.getIncomingEmbeddablePackage(CANVAS_APP, true); - - useEffect(() => { - if (isByValueEnabled && incomingEmbeddable) { - const { embeddableId, input: incomingInput, type } = incomingEmbeddable; - - // retrieve existing element - const originalElement = selectedPage.elements.find( - ({ id }: CanvasElement) => id === embeddableId - ); - - if (originalElement) { - const originalAst = fromExpression(originalElement!.expression); - - const functionIndex = originalAst.chain.findIndex( - ({ function: fn }) => fn === 'embeddable' - ); - - const originalInput = decode( - originalAst.chain[functionIndex].arguments.config[0] as string - ); - - // clear out resolved arg for old embeddable - const argumentPath = [embeddableId, 'expressionRenderable']; - dispatch(clearValue({ path: argumentPath })); - - const updatedInput = { ...originalInput, ...incomingInput }; - - const expression = `embeddable config="${encode(updatedInput)}" - type="${type}" -| render`; - - dispatch( - updateEmbeddableExpression({ - elementId: originalElement.id, - embeddableExpression: expression, - }) - ); - - // update resolved args - dispatch(fetchEmbeddableRenderable(originalElement.id)); - - // select new embeddable element - dispatch(selectToplevelNodes([embeddableId])); - } else { - const expression = `embeddable config="${encode(incomingInput)}" - type="${type}" -| render`; - dispatch(addElement(selectedPage.id, { expression })); - } - } - }, [dispatch, selectedPage, incomingEmbeddable, isByValueEnabled]); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx index 7cc077203c7372..622c885b6ef281 100644 --- a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx +++ b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx @@ -27,7 +27,6 @@ import { WorkpadRoutingContext } from '../../routes/workpad'; import { usePlatformService } from '../../services'; import { Workpad as WorkpadComponent, Props } from './workpad.component'; import { State } from '../../../types'; -import { useIncomingEmbeddable } from '../hooks'; type ContainerProps = Pick; @@ -59,9 +58,6 @@ export const Workpad: FC = (props) => { }; }); - const selectedPage = propsFromState.pages[propsFromState.selectedPageNumber - 1]; - useIncomingEmbeddable(selectedPage); - const fetchAllRenderables = useCallback(() => { dispatch(fetchAllRenderablesAction()); }, [dispatch]); diff --git a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss index 0ddd44ed8f9a81..4acdca10d61cc2 100644 --- a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss +++ b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss @@ -31,7 +31,7 @@ $canvasLayoutFontSize: $euiFontSizeS; .canvasLayout__stageHeader { flex-grow: 0; flex-basis: auto; - padding: $euiSizeS $euiSize; + padding: $euiSizeS; font-size: $canvasLayoutFontSize; border-bottom: $euiBorderThin; background: $euiColorLightestShade; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot deleted file mode 100644 index f4aab0e59e7ee6..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot +++ /dev/null @@ -1,81 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Storyshots components/WorkpadHeader/EditorMenu dark mode 1`] = ` -
-
- -
-
-`; - -exports[`Storyshots components/WorkpadHeader/EditorMenu default 1`] = ` -
-
- -
-
-`; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx deleted file mode 100644 index 01048bc0af3010..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx +++ /dev/null @@ -1,107 +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 { storiesOf } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import React from 'react'; -import { EmbeddableFactoryDefinition, IEmbeddable } from 'src/plugins/embeddable/public'; -import { BaseVisType, VisTypeAlias } from 'src/plugins/visualizations/public'; -import { EditorMenu } from '../editor_menu.component'; - -const testFactories: EmbeddableFactoryDefinition[] = [ - { - type: 'ml_anomaly_swimlane', - getDisplayName: () => 'Anomaly swimlane', - getIconType: () => '', - getDescription: () => 'Description for anomaly swimlane', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'swimlane_embeddable' } as IEmbeddable), - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'ml_anomaly_chart', - getDisplayName: () => 'Anomaly chart', - getIconType: () => '', - getDescription: () => 'Description for anomaly chart', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'log_stream', - getDisplayName: () => 'Log stream', - getIconType: () => '', - getDescription: () => 'Description for log stream', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - }, -]; - -const testVisTypes: BaseVisType[] = [ - { title: 'TSVB', icon: '', description: 'Description of TSVB', name: 'tsvb' } as BaseVisType, - { - titleInWizard: 'Custom visualization', - title: 'Vega', - icon: '', - description: 'Description of Vega', - name: 'vega', - } as BaseVisType, -]; - -const testVisTypeAliases: VisTypeAlias[] = [ - { - title: 'Lens', - aliasApp: 'lens', - aliasPath: 'path/to/lens', - icon: 'lensApp', - name: 'lens', - description: 'Description of Lens app', - stage: 'production', - }, - { - title: 'Maps', - aliasApp: 'maps', - aliasPath: 'path/to/maps', - icon: 'gisApp', - name: 'maps', - description: 'Description of Maps app', - stage: 'production', - }, -]; - -storiesOf('components/WorkpadHeader/EditorMenu', module) - .add('default', () => ( - action('createNewVisType')} - createNewEmbeddable={() => action('createNewEmbeddable')} - /> - )) - .add('dark mode', () => ( - action('createNewVisType')} - createNewEmbeddable={() => action('createNewEmbeddable')} - /> - )); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx deleted file mode 100644 index e8f762f9731a19..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx +++ /dev/null @@ -1,170 +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 React, { FC } from 'react'; -import { - EuiContextMenu, - EuiContextMenuPanelItemDescriptor, - EuiContextMenuItemIcon, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { EmbeddableFactoryDefinition } from '../../../../../../../src/plugins/embeddable/public'; -import { BaseVisType, VisTypeAlias } from '../../../../../../../src/plugins/visualizations/public'; -import { SolutionToolbarPopover } from '../../../../../../../src/plugins/presentation_util/public'; - -const strings = { - getEditorMenuButtonLabel: () => - i18n.translate('xpack.canvas.solutionToolbar.editorMenuButtonLabel', { - defaultMessage: 'Select type', - }), -}; - -interface FactoryGroup { - id: string; - appName: string; - icon: EuiContextMenuItemIcon; - panelId: number; - factories: EmbeddableFactoryDefinition[]; -} - -interface Props { - factories: EmbeddableFactoryDefinition[]; - isDarkThemeEnabled?: boolean; - promotedVisTypes: BaseVisType[]; - visTypeAliases: VisTypeAlias[]; - createNewVisType: (visType?: BaseVisType | VisTypeAlias) => () => void; - createNewEmbeddable: (factory: EmbeddableFactoryDefinition) => () => void; -} - -export const EditorMenu: FC = ({ - factories, - isDarkThemeEnabled, - promotedVisTypes, - visTypeAliases, - createNewVisType, - createNewEmbeddable, -}: Props) => { - const factoryGroupMap: Record = {}; - const ungroupedFactories: EmbeddableFactoryDefinition[] = []; - - let panelCount = 1; - - // Maps factories with a group to create nested context menus for each group type - // and pushes ungrouped factories into a separate array - factories.forEach((factory: EmbeddableFactoryDefinition, index) => { - const { grouping } = factory; - - if (grouping) { - grouping.forEach((group) => { - if (factoryGroupMap[group.id]) { - factoryGroupMap[group.id].factories.push(factory); - } else { - factoryGroupMap[group.id] = { - id: group.id, - appName: group.getDisplayName ? group.getDisplayName({}) : group.id, - icon: (group.getIconType ? group.getIconType({}) : 'empty') as EuiContextMenuItemIcon, - factories: [factory], - panelId: panelCount, - }; - - panelCount++; - } - }); - } else { - ungroupedFactories.push(factory); - } - }); - - const getVisTypeMenuItem = (visType: BaseVisType): EuiContextMenuPanelItemDescriptor => { - const { name, title, titleInWizard, description, icon = 'empty' } = visType; - return { - name: titleInWizard || title, - icon: icon as string, - onClick: createNewVisType(visType), - 'data-test-subj': `visType-${name}`, - toolTipContent: description, - }; - }; - - const getVisTypeAliasMenuItem = ( - visTypeAlias: VisTypeAlias - ): EuiContextMenuPanelItemDescriptor => { - const { name, title, description, icon = 'empty' } = visTypeAlias; - - return { - name: title, - icon, - onClick: createNewVisType(visTypeAlias), - 'data-test-subj': `visType-${name}`, - toolTipContent: description, - }; - }; - - const getEmbeddableFactoryMenuItem = ( - factory: EmbeddableFactoryDefinition - ): EuiContextMenuPanelItemDescriptor => { - const icon = factory?.getIconType ? factory.getIconType() : 'empty'; - - const toolTipContent = factory?.getDescription ? factory.getDescription() : undefined; - - return { - name: factory.getDisplayName(), - icon, - toolTipContent, - onClick: createNewEmbeddable(factory), - 'data-test-subj': `createNew-${factory.type}`, - }; - }; - - const editorMenuPanels = [ - { - id: 0, - items: [ - ...visTypeAliases.map(getVisTypeAliasMenuItem), - ...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({ - name: appName, - icon, - panel: panelId, - 'data-test-subj': `canvasEditorMenu-${id}Group`, - })), - ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), - ...promotedVisTypes.map(getVisTypeMenuItem), - ], - }, - ...Object.values(factoryGroupMap).map( - ({ appName, panelId, factories: groupFactories }: FactoryGroup) => ({ - id: panelId, - title: appName, - items: groupFactories.map(getEmbeddableFactoryMenuItem), - }) - ), - ]; - - return ( - - {() => ( - - )} - - ); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx deleted file mode 100644 index dad34e6983c5db..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx +++ /dev/null @@ -1,147 +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 React, { FC, useCallback } from 'react'; -import { useLocation } from 'react-router-dom'; -import { trackCanvasUiMetric, METRIC_TYPE } from '../../../../public/lib/ui_metric'; -import { - useEmbeddablesService, - usePlatformService, - useVisualizationsService, -} from '../../../services'; -import { - BaseVisType, - VisGroups, - VisTypeAlias, -} from '../../../../../../../src/plugins/visualizations/public'; -import { - EmbeddableFactoryDefinition, - EmbeddableInput, -} from '../../../../../../../src/plugins/embeddable/public'; -import { CANVAS_APP } from '../../../../common/lib'; -import { encode } from '../../../../common/lib/embeddable_dataurl'; -import { ElementSpec } from '../../../../types'; -import { EditorMenu as Component } from './editor_menu.component'; - -interface Props { - /** - * Handler for adding a selected element to the workpad - */ - addElement: (element: Partial) => void; -} - -export const EditorMenu: FC = ({ addElement }) => { - const embeddablesService = useEmbeddablesService(); - const { pathname, search } = useLocation(); - const platformService = usePlatformService(); - const stateTransferService = embeddablesService.getStateTransfer(); - const visualizationsService = useVisualizationsService(); - const IS_DARK_THEME = platformService.getUISetting('theme:darkMode'); - - const createNewVisType = useCallback( - (visType?: BaseVisType | VisTypeAlias) => () => { - let path = ''; - let appId = ''; - - if (visType) { - if (trackCanvasUiMetric) { - trackCanvasUiMetric(METRIC_TYPE.CLICK, `${visType.name}:create`); - } - - if ('aliasPath' in visType) { - appId = visType.aliasApp; - path = visType.aliasPath; - } else { - appId = 'visualize'; - path = `#/create?type=${encodeURIComponent(visType.name)}`; - } - } else { - appId = 'visualize'; - path = '#/create?'; - } - - stateTransferService.navigateToEditor(appId, { - path, - state: { - originatingApp: CANVAS_APP, - originatingPath: `#/${pathname}${search}`, - }, - }); - }, - [stateTransferService, pathname, search] - ); - - const createNewEmbeddable = useCallback( - (factory: EmbeddableFactoryDefinition) => async () => { - if (trackCanvasUiMetric) { - trackCanvasUiMetric(METRIC_TYPE.CLICK, factory.type); - } - let embeddableInput; - if (factory.getExplicitInput) { - embeddableInput = await factory.getExplicitInput(); - } else { - const newEmbeddable = await factory.create({} as EmbeddableInput); - embeddableInput = newEmbeddable?.getInput(); - } - - if (embeddableInput) { - const config = encode(embeddableInput); - const expression = `embeddable config="${config}" - type="${factory.type}" -| render`; - - addElement({ expression }); - } - }, - [addElement] - ); - - const getVisTypesByGroup = (group: VisGroups): BaseVisType[] => - visualizationsService - .getByGroup(group) - .sort(({ name: a }: BaseVisType | VisTypeAlias, { name: b }: BaseVisType | VisTypeAlias) => { - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - return 0; - }) - .filter(({ hidden }: BaseVisType) => !hidden); - - const visTypeAliases = visualizationsService - .getAliases() - .sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => - a === b ? 0 : a ? -1 : 1 - ); - - const factories = embeddablesService - ? Array.from(embeddablesService.getEmbeddableFactories()).filter( - ({ type, isEditable, canCreateNew, isContainerType }) => - isEditable() && - !isContainerType && - canCreateNew() && - !['visualization', 'ml'].some((factoryType) => { - return type.includes(factoryType); - }) - ) - : []; - - const promotedVisTypes = getVisTypesByGroup(VisGroups.PROMOTED); - - return ( - - ); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts deleted file mode 100644 index 0f903b1bbbe2ed..00000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { EditorMenu } from './editor_menu'; -export { EditorMenu as EditorMenuComponent } from './editor_menu.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx index 1cfab236d9a9c9..8ac581b0866a46 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx @@ -12,11 +12,11 @@ import { EuiContextMenu, EuiIcon, EuiContextMenuPanelItemDescriptor } from '@ela import { i18n } from '@kbn/i18n'; import { PrimaryActionPopover } from '../../../../../../../src/plugins/presentation_util/public'; import { getId } from '../../../lib/get_id'; +import { ClosePopoverFn } from '../../popover'; import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib'; import { ElementSpec } from '../../../../types'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; import { AssetManager } from '../../asset_manager'; -import { ClosePopoverFn } from '../../popover'; import { SavedElementsModal } from '../../saved_elements_modal'; interface CategorizedElementLists { @@ -112,7 +112,7 @@ const categorizeElementsByType = (elements: ElementSpec[]): { [key: string]: Ele return categories; }; -export interface Props { +interface Props { /** * Dictionary of elements from elements registry */ @@ -120,7 +120,7 @@ export interface Props { /** * Handler for adding a selected element to the workpad */ - addElement: (element: Partial) => void; + addElement: (element: ElementSpec) => void; } export const ElementMenu: FunctionComponent = ({ elements, addElement }) => { diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts index 037bb84b0cdba4..52c8daece7690a 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { ElementMenu } from './element_menu.component'; +export { ElementMenu } from './element_menu'; +export { ElementMenu as ElementMenuComponent } from './element_menu.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx index b84e4faf2925e7..f031d7c2631991 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx @@ -27,7 +27,6 @@ import { ElementMenu } from './element_menu'; import { ShareMenu } from './share_menu'; import { ViewMenu } from './view_menu'; import { LabsControl } from './labs_control'; -import { EditorMenu } from './editor_menu'; const strings = { getFullScreenButtonAriaLabel: () => @@ -161,22 +160,24 @@ export const WorkpadHeader: FC = ({ - {isWriteable && ( - - - {{ - primaryActionButton: , - quickButtonGroup: , - addFromLibraryButton: , - extraButtons: [], - }} - - - )} + {isWriteable && ( + + + {{ + primaryActionButton: ( + + ), + quickButtonGroup: , + addFromLibraryButton: , + }} + + + )} @@ -191,7 +192,6 @@ export const WorkpadHeader: FC = ({ - diff --git a/x-pack/plugins/canvas/public/plugin.tsx b/x-pack/plugins/canvas/public/plugin.tsx index 912055dd47a627..723d1afea2860f 100644 --- a/x-pack/plugins/canvas/public/plugin.tsx +++ b/x-pack/plugins/canvas/public/plugin.tsx @@ -8,7 +8,6 @@ import { BehaviorSubject } from 'rxjs'; import type { SharePluginSetup } from 'src/plugins/share/public'; import { ChartsPluginSetup, ChartsPluginStart } from 'src/plugins/charts/public'; -import { VisualizationsStart } from 'src/plugins/visualizations/public'; import { ReportingStart } from '../../reporting/public'; import { CoreSetup, @@ -64,7 +63,6 @@ export interface CanvasStartDeps { charts: ChartsPluginStart; data: DataPublicPluginStart; presentationUtil: PresentationUtilPluginStart; - visualizations: VisualizationsStart; spaces?: SpacesPluginStart; } @@ -124,12 +122,7 @@ export class CanvasPlugin const { pluginServices } = await import('./services'); pluginServices.setRegistry( - pluginServiceRegistry.start({ - coreStart, - startPlugins, - appUpdater: this.appUpdater, - initContext: this.initContext, - }) + pluginServiceRegistry.start({ coreStart, startPlugins, initContext: this.initContext }) ); // Load application bundle diff --git a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts index bd9a4e7141c272..35e79b442a15d7 100644 --- a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts +++ b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts @@ -53,24 +53,14 @@ export const useWorkpad = ( workpad.aliasId = aliasId; } - if (storedWorkpad.id !== workpadId || storedWorkpad.aliasId !== aliasId) { - dispatch(setAssets(assets)); - dispatch(setWorkpad(workpad, { loadPages })); - dispatch(setZoomScale(1)); - } + dispatch(setAssets(assets)); + dispatch(setWorkpad(workpad, { loadPages })); + dispatch(setZoomScale(1)); } catch (e) { setError(e as Error | string); } })(); - }, [ - workpadId, - dispatch, - setError, - loadPages, - workpadResolve, - storedWorkpad.id, - storedWorkpad.aliasId, - ]); + }, [workpadId, dispatch, setError, loadPages, workpadResolve]); useEffect(() => { (() => { diff --git a/x-pack/plugins/canvas/public/services/embeddables.ts b/x-pack/plugins/canvas/public/services/embeddables.ts index 26b150b7a53493..24d7a57e086f2d 100644 --- a/x-pack/plugins/canvas/public/services/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/embeddables.ts @@ -5,12 +5,8 @@ * 2.0. */ -import { - EmbeddableFactory, - EmbeddableStateTransfer, -} from '../../../../../src/plugins/embeddable/public'; +import { EmbeddableFactory } from '../../../../../src/plugins/embeddable/public'; export interface CanvasEmbeddablesService { getEmbeddableFactories: () => IterableIterator; - getStateTransfer: () => EmbeddableStateTransfer; } diff --git a/x-pack/plugins/canvas/public/services/index.ts b/x-pack/plugins/canvas/public/services/index.ts index ed55f919e4c767..f4292810b80896 100644 --- a/x-pack/plugins/canvas/public/services/index.ts +++ b/x-pack/plugins/canvas/public/services/index.ts @@ -17,7 +17,6 @@ import { CanvasNavLinkService } from './nav_link'; import { CanvasNotifyService } from './notify'; import { CanvasPlatformService } from './platform'; import { CanvasReportingService } from './reporting'; -import { CanvasVisualizationsService } from './visualizations'; import { CanvasWorkpadService } from './workpad'; export interface CanvasPluginServices { @@ -29,7 +28,6 @@ export interface CanvasPluginServices { notify: CanvasNotifyService; platform: CanvasPlatformService; reporting: CanvasReportingService; - visualizations: CanvasVisualizationsService; workpad: CanvasWorkpadService; } @@ -46,6 +44,4 @@ export const useNavLinkService = () => (() => pluginServices.getHooks().navLink. export const useNotifyService = () => (() => pluginServices.getHooks().notify.useService())(); export const usePlatformService = () => (() => pluginServices.getHooks().platform.useService())(); export const useReportingService = () => (() => pluginServices.getHooks().reporting.useService())(); -export const useVisualizationsService = () => - (() => pluginServices.getHooks().visualizations.useService())(); export const useWorkpadService = () => (() => pluginServices.getHooks().workpad.useService())(); diff --git a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts index 8d1a86edab3d89..054b9da7409fbb 100644 --- a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts @@ -16,5 +16,4 @@ export type EmbeddablesServiceFactory = KibanaPluginServiceFactory< export const embeddablesServiceFactory: EmbeddablesServiceFactory = ({ startPlugins }) => ({ getEmbeddableFactories: startPlugins.embeddable.getEmbeddableFactories, - getStateTransfer: startPlugins.embeddable.getStateTransfer, }); diff --git a/x-pack/plugins/canvas/public/services/kibana/index.ts b/x-pack/plugins/canvas/public/services/kibana/index.ts index 91767947bc0a65..1eb010e8d6f9da 100644 --- a/x-pack/plugins/canvas/public/services/kibana/index.ts +++ b/x-pack/plugins/canvas/public/services/kibana/index.ts @@ -22,7 +22,6 @@ import { navLinkServiceFactory } from './nav_link'; import { notifyServiceFactory } from './notify'; import { platformServiceFactory } from './platform'; import { reportingServiceFactory } from './reporting'; -import { visualizationsServiceFactory } from './visualizations'; import { workpadServiceFactory } from './workpad'; export { customElementServiceFactory } from './custom_element'; @@ -32,7 +31,6 @@ export { labsServiceFactory } from './labs'; export { notifyServiceFactory } from './notify'; export { platformServiceFactory } from './platform'; export { reportingServiceFactory } from './reporting'; -export { visualizationsServiceFactory } from './visualizations'; export { workpadServiceFactory } from './workpad'; export const pluginServiceProviders: PluginServiceProviders< @@ -47,7 +45,6 @@ export const pluginServiceProviders: PluginServiceProviders< notify: new PluginServiceProvider(notifyServiceFactory), platform: new PluginServiceProvider(platformServiceFactory), reporting: new PluginServiceProvider(reportingServiceFactory), - visualizations: new PluginServiceProvider(visualizationsServiceFactory), workpad: new PluginServiceProvider(workpadServiceFactory), }; diff --git a/x-pack/plugins/canvas/public/services/kibana/visualizations.ts b/x-pack/plugins/canvas/public/services/kibana/visualizations.ts deleted file mode 100644 index e319ec1c1f4272..00000000000000 --- a/x-pack/plugins/canvas/public/services/kibana/visualizations.ts +++ /dev/null @@ -1,21 +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 { KibanaPluginServiceFactory } from '../../../../../../src/plugins/presentation_util/public'; -import { CanvasStartDeps } from '../../plugin'; -import { CanvasVisualizationsService } from '../visualizations'; - -export type VisualizationsServiceFactory = KibanaPluginServiceFactory< - CanvasVisualizationsService, - CanvasStartDeps ->; - -export const visualizationsServiceFactory: VisualizationsServiceFactory = ({ startPlugins }) => ({ - showNewVisModal: startPlugins.visualizations.showNewVisModal, - getByGroup: startPlugins.visualizations.getByGroup, - getAliases: startPlugins.visualizations.getAliases, -}); diff --git a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts index 9c2cf4d0650abe..173d27563e2b2a 100644 --- a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts @@ -14,5 +14,4 @@ const noop = (..._args: any[]): any => {}; export const embeddablesServiceFactory: EmbeddablesServiceFactory = () => ({ getEmbeddableFactories: noop, - getStateTransfer: noop, }); diff --git a/x-pack/plugins/canvas/public/services/stubs/index.ts b/x-pack/plugins/canvas/public/services/stubs/index.ts index 2216013a29c129..06a5ff49e9c04e 100644 --- a/x-pack/plugins/canvas/public/services/stubs/index.ts +++ b/x-pack/plugins/canvas/public/services/stubs/index.ts @@ -22,7 +22,6 @@ import { navLinkServiceFactory } from './nav_link'; import { notifyServiceFactory } from './notify'; import { platformServiceFactory } from './platform'; import { reportingServiceFactory } from './reporting'; -import { visualizationsServiceFactory } from './visualizations'; import { workpadServiceFactory } from './workpad'; export { customElementServiceFactory } from './custom_element'; @@ -32,7 +31,6 @@ export { navLinkServiceFactory } from './nav_link'; export { notifyServiceFactory } from './notify'; export { platformServiceFactory } from './platform'; export { reportingServiceFactory } from './reporting'; -export { visualizationsServiceFactory } from './visualizations'; export { workpadServiceFactory } from './workpad'; export const pluginServiceProviders: PluginServiceProviders = { @@ -44,7 +42,6 @@ export const pluginServiceProviders: PluginServiceProviders; - -const noop = (..._args: any[]): any => {}; - -export const visualizationsServiceFactory: VisualizationsServiceFactory = () => ({ - showNewVisModal: noop, - getByGroup: noop, - getAliases: noop, -}); diff --git a/x-pack/plugins/canvas/public/services/visualizations.ts b/x-pack/plugins/canvas/public/services/visualizations.ts deleted file mode 100644 index c602b1dd39f3d2..00000000000000 --- a/x-pack/plugins/canvas/public/services/visualizations.ts +++ /dev/null @@ -1,14 +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 { VisualizationsStart } from '../../../../../src/plugins/visualizations/public'; - -export interface CanvasVisualizationsService { - showNewVisModal: VisualizationsStart['showNewVisModal']; - getByGroup: VisualizationsStart['getByGroup']; - getAliases: VisualizationsStart['getAliases']; -} diff --git a/x-pack/plugins/canvas/public/state/reducers/embeddable.ts b/x-pack/plugins/canvas/public/state/reducers/embeddable.ts index 092d4300d86b79..4cfdc7f21945f6 100644 --- a/x-pack/plugins/canvas/public/state/reducers/embeddable.ts +++ b/x-pack/plugins/canvas/public/state/reducers/embeddable.ts @@ -40,7 +40,7 @@ export const embeddableReducer = handleActions< const element = pageWithElement.elements.find((elem) => elem.id === elementId); - if (!element || element.expression === embeddableExpression) { + if (!element) { return workpadState; } diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index ebe43ba76a46ac..4071b891e4c3d7 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -14,7 +14,6 @@ import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { BfetchServerSetup } from 'src/plugins/bfetch/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { HomeServerPluginSetup } from 'src/plugins/home/server'; -import { EmbeddableSetup } from 'src/plugins/embeddable/server'; import { ESSQL_SEARCH_STRATEGY } from '../common/lib/constants'; import { ReportingSetup } from '../../reporting/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; @@ -31,7 +30,6 @@ import { CanvasRouteHandlerContext, createWorkpadRouteContext } from './workpad_ interface PluginsSetup { expressions: ExpressionsServerSetup; - embeddable: EmbeddableSetup; features: FeaturesPluginSetup; home: HomeServerPluginSetup; bfetch: BfetchServerSetup; @@ -84,12 +82,7 @@ export class CanvasPlugin implements Plugin { const kibanaIndex = coreSetup.savedObjects.getKibanaIndex(); registerCanvasUsageCollector(plugins.usageCollection, kibanaIndex); - setupInterpreter(expressionsFork, { - embeddablePersistableStateService: { - extract: plugins.embeddable.extract, - inject: plugins.embeddable.inject, - }, - }); + setupInterpreter(expressionsFork); coreSetup.getStartServices().then(([_, depsStart]) => { const strategy = essqlSearchStrategyProvider(); diff --git a/x-pack/plugins/canvas/server/setup_interpreter.ts b/x-pack/plugins/canvas/server/setup_interpreter.ts index 849ad79717056c..2fe23eb86c086f 100644 --- a/x-pack/plugins/canvas/server/setup_interpreter.ts +++ b/x-pack/plugins/canvas/server/setup_interpreter.ts @@ -7,15 +7,9 @@ import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { functions } from '../canvas_plugin_src/functions/server'; -import { - initFunctions as initExternalFunctions, - InitializeArguments, -} from '../canvas_plugin_src/functions/external'; +import { functions as externalFunctions } from '../canvas_plugin_src/functions/external'; -export function setupInterpreter( - expressions: ExpressionsServerSetup, - dependencies: InitializeArguments -) { +export function setupInterpreter(expressions: ExpressionsServerSetup) { functions.forEach((f) => expressions.registerFunction(f)); - initExternalFunctions(dependencies).forEach((f) => expressions.registerFunction(f)); + externalFunctions.forEach((f) => expressions.registerFunction(f)); } diff --git a/x-pack/plugins/canvas/types/embeddables.ts b/x-pack/plugins/canvas/types/embeddables.ts deleted file mode 100644 index b78efece59d8f8..00000000000000 --- a/x-pack/plugins/canvas/types/embeddables.ts +++ /dev/null @@ -1,16 +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 { TimeRange } from 'src/plugins/data/public'; -import { Filter } from '@kbn/es-query'; -import { EmbeddableInput as Input } from '../../../../src/plugins/embeddable/common/'; - -export type EmbeddableInput = Input & { - timeRange?: TimeRange; - filters?: Filter[]; - savedObjectId?: string; -}; diff --git a/x-pack/plugins/canvas/types/functions.ts b/x-pack/plugins/canvas/types/functions.ts index c80102915ed953..2569e0b10685b7 100644 --- a/x-pack/plugins/canvas/types/functions.ts +++ b/x-pack/plugins/canvas/types/functions.ts @@ -10,8 +10,8 @@ import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; import { functions as commonFunctions } from '../canvas_plugin_src/functions/common'; import { functions as browserFunctions } from '../canvas_plugin_src/functions/browser'; import { functions as serverFunctions } from '../canvas_plugin_src/functions/server'; -import { initFunctions as initExternalFunctions } from '../canvas_plugin_src/functions/external'; -import { initFunctions as initClientFunctions } from '../public/functions'; +import { functions as externalFunctions } from '../canvas_plugin_src/functions/external'; +import { initFunctions } from '../public/functions'; /** * A `ExpressionFunctionFactory` is a powerful type used for any function that produces @@ -90,11 +90,9 @@ export type FunctionFactory = type CommonFunction = FunctionFactory; type BrowserFunction = FunctionFactory; type ServerFunction = FunctionFactory; -type ExternalFunction = FunctionFactory< - ReturnType extends Array ? U : never ->; +type ExternalFunction = FunctionFactory; type ClientFunctions = FunctionFactory< - ReturnType extends Array ? U : never + ReturnType extends Array ? U : never >; /** diff --git a/x-pack/plugins/canvas/types/index.ts b/x-pack/plugins/canvas/types/index.ts index 930f3372920884..09ae1510be6da0 100644 --- a/x-pack/plugins/canvas/types/index.ts +++ b/x-pack/plugins/canvas/types/index.ts @@ -9,7 +9,6 @@ export * from '../../../../src/plugins/expressions/common'; export * from './assets'; export * from './canvas'; export * from './elements'; -export * from './embeddables'; export * from './filters'; export * from './functions'; export * from './renderers'; diff --git a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx index 1fed1d90689be8..990d44584cf055 100644 --- a/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/cases/public/components/configure_cases/index.test.tsx @@ -47,6 +47,7 @@ describe('ConfigureCases', () => { iconClass: 'logoSecurity', }); }); + beforeEach(() => { useActionTypesMock.mockImplementation(() => useActionTypesResponse); }); @@ -451,150 +452,149 @@ describe('ConfigureCases', () => { ).toBe('Update My Connector 2'); }); }); -}); -// Failing: See https://github.com/elastic/kibana/issues/115366 -describe.skip('closure options', () => { - let wrapper: ReactWrapper; - let persistCaseConfigure: jest.Mock; + describe('closure options', () => { + let wrapper: ReactWrapper; + let persistCaseConfigure: jest.Mock; - beforeEach(() => { - persistCaseConfigure = jest.fn(); - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'servicenow-1', - name: 'My connector', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - currentConfiguration: { + beforeEach(() => { + persistCaseConfigure = jest.fn(); + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + mapping: null, + closureType: 'close-by-user', connector: { - id: 'My connector', + id: 'servicenow-1', name: 'My connector', - type: ConnectorTypes.jira, + type: ConnectorTypes.serviceNowITSM, fields: null, }, - closureType: 'close-by-user', - }, - persistCaseConfigure, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); + currentConfiguration: { + connector: { + id: 'My connector', + name: 'My connector', + type: ConnectorTypes.jira, + fields: null, + }, + closureType: 'close-by-user', + }, + persistCaseConfigure, + })); + useConnectorsMock.mockImplementation(() => useConnectorsResponse); + useGetUrlSearchMock.mockImplementation(() => searchURL); - wrapper = mount(, { - wrappingComponent: TestProviders, + wrapper = mount(, { + wrappingComponent: TestProviders, + }); }); - }); - test('it submits the configuration correctly when changing closure type', () => { - wrapper.find('input[id="close-by-pushing"]').simulate('change'); - wrapper.update(); + test('it submits the configuration correctly when changing closure type', () => { + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); - expect(persistCaseConfigure).toHaveBeenCalled(); - expect(persistCaseConfigure).toHaveBeenCalledWith({ - connector: { - id: 'servicenow-1', - name: 'My connector', - type: ConnectorTypes.serviceNowITSM, - fields: null, - }, - closureType: 'close-by-pushing', + expect(persistCaseConfigure).toHaveBeenCalled(); + expect(persistCaseConfigure).toHaveBeenCalledWith({ + connector: { + id: 'servicenow-1', + name: 'My connector', + type: ConnectorTypes.serviceNowITSM, + fields: null, + }, + closureType: 'close-by-pushing', + }); }); }); -}); -describe('user interactions', () => { - beforeEach(() => { - useCaseConfigureMock.mockImplementation(() => ({ - ...useCaseConfigureResponse, - mapping: null, - closureType: 'close-by-user', - connector: { - id: 'resilient-2', - name: 'unchanged', - type: ConnectorTypes.resilient, - fields: null, - }, - currentConfiguration: { + describe('user interactions', () => { + beforeEach(() => { + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + mapping: null, + closureType: 'close-by-user', connector: { id: 'resilient-2', name: 'unchanged', - type: ConnectorTypes.serviceNowITSM, + type: ConnectorTypes.resilient, fields: null, }, - closureType: 'close-by-user', - }, - })); - useConnectorsMock.mockImplementation(() => useConnectorsResponse); - useGetUrlSearchMock.mockImplementation(() => searchURL); - }); - - test('it show the add flyout when pressing the add connector button', async () => { - const wrapper = mount(, { - wrappingComponent: TestProviders, + currentConfiguration: { + connector: { + id: 'resilient-2', + name: 'unchanged', + type: ConnectorTypes.serviceNowITSM, + fields: null, + }, + closureType: 'close-by-user', + }, + })); + useConnectorsMock.mockImplementation(() => useConnectorsResponse); + useGetUrlSearchMock.mockImplementation(() => searchURL); }); - wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); - wrapper.find('button[data-test-subj="dropdown-connector-add-connector"]').simulate('click'); + test('it show the add flyout when pressing the add connector button', async () => { + const wrapper = mount(, { + wrappingComponent: TestProviders, + }); - await waitFor(() => { - wrapper.update(); - expect(wrapper.find('ConnectorAddFlyout').exists()).toBe(true); - expect(wrapper.find('ConnectorAddFlyout').prop('actionTypes')).toEqual([ - expect.objectContaining({ - id: '.servicenow', - }), - expect.objectContaining({ - id: '.jira', - }), - expect.objectContaining({ - id: '.resilient', - }), - expect.objectContaining({ - id: '.servicenow-sir', - }), - ]); + wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + wrapper.find('button[data-test-subj="dropdown-connector-add-connector"]').simulate('click'); + + await waitFor(() => { + wrapper.update(); + expect(wrapper.find('ConnectorAddFlyout').exists()).toBe(true); + expect(wrapper.find('ConnectorAddFlyout').prop('actionTypes')).toEqual([ + expect.objectContaining({ + id: '.servicenow', + }), + expect.objectContaining({ + id: '.jira', + }), + expect.objectContaining({ + id: '.resilient', + }), + expect.objectContaining({ + id: '.servicenow-sir', + }), + ]); + }); }); - }); - test('it show the edit flyout when pressing the update connector button', async () => { - const actionType = actionTypeRegistryMock.createMockActionTypeModel({ - id: '.resilient', - validateConnector: () => { - return Promise.resolve({}); - }, - validateParams: () => { - const validationResult = { errors: {} }; - return Promise.resolve(validationResult); - }, - actionConnectorFields: null, - }); - - useKibanaMock().services.triggersActionsUi.actionTypeRegistry.get = jest - .fn() - .mockReturnValue(actionType); - useKibanaMock().services.triggersActionsUi.actionTypeRegistry.has = jest - .fn() - .mockReturnValue(true); - - const wrapper = mount(, { - wrappingComponent: TestProviders, - }); - wrapper - .find('button[data-test-subj="case-configure-update-selected-connector-button"]') - .simulate('click'); - - await waitFor(() => { - wrapper.update(); - expect(wrapper.find('ConnectorEditFlyout').exists()).toBe(true); - expect(wrapper.find('ConnectorEditFlyout').prop('initialConnector')).toEqual(connectors[1]); - }); + test('it show the edit flyout when pressing the update connector button', async () => { + const actionType = actionTypeRegistryMock.createMockActionTypeModel({ + id: '.resilient', + validateConnector: () => { + return Promise.resolve({}); + }, + validateParams: () => { + const validationResult = { errors: {} }; + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + }); + + useKibanaMock().services.triggersActionsUi.actionTypeRegistry.get = jest + .fn() + .mockReturnValue(actionType); + useKibanaMock().services.triggersActionsUi.actionTypeRegistry.has = jest + .fn() + .mockReturnValue(true); + + const wrapper = mount(, { + wrappingComponent: TestProviders, + }); + wrapper + .find('button[data-test-subj="case-configure-update-selected-connector-button"]') + .simulate('click'); + + await waitFor(() => { + wrapper.update(); + expect(wrapper.find('ConnectorEditFlyout').exists()).toBe(true); + expect(wrapper.find('ConnectorEditFlyout').prop('initialConnector')).toEqual(connectors[1]); + }); - expect( - wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() - ).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx index c0278c765e85ee..9598212d3e0c98 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx @@ -5,9 +5,6 @@ * 2.0. */ -import '../../../__mocks__/shallow_useeffect.mock'; -import '../../../__mocks__/react_router'; -import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; import '../../__mocks__/engine_logic.mock'; import React from 'react'; @@ -15,110 +12,13 @@ import { Route, Switch } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { LogRetentionOptions } from '../log_retention'; - import { CurationsRouter } from './'; -const MOCK_VALUES = { - // CurationsSettingsLogic - dataLoading: false, - curationsSettings: { - enabled: true, - mode: 'automatic', - }, - // LogRetentionLogic - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: true, - }, - }, - // LicensingLogic - hasPlatinumLicense: true, -}; - -const MOCK_ACTIONS = { - // CurationsSettingsLogic - loadCurationsSettings: jest.fn(), - onSkipLoadingCurationsSettings: jest.fn(), - // LogRetentionLogic - fetchLogRetention: jest.fn(), -}; - describe('CurationsRouter', () => { - beforeEach(() => { - jest.clearAllMocks(); - setMockActions(MOCK_ACTIONS); - }); - it('renders', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); expect(wrapper.find(Route)).toHaveLength(4); }); - - it('loads log retention settings', () => { - setMockValues(MOCK_VALUES); - shallow(); - - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); - }); - - describe('when the user has no platinum license', () => { - beforeEach(() => { - setMockValues({ - ...MOCK_VALUES, - hasPlatinumLicense: false, - }); - }); - - it('it does not fetch log retention', () => { - shallow(); - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); - }); - }); - - describe('loading curation settings based on log retention', () => { - it('loads curation settings when log retention is enabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: true, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('skips loading curation settings when log retention is disabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: false, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('takes no action if log retention has not yet been loaded', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: null, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx index a3b000ea5054ac..693e5406b714b3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx @@ -5,53 +5,20 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { Route, Switch } from 'react-router-dom'; -import { useValues, useActions } from 'kea'; - -import { LicensingLogic } from '../../../shared/licensing'; import { ENGINE_CURATIONS_PATH, ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH, ENGINE_CURATION_SUGGESTION_PATH, } from '../../routes'; -import { LogRetentionLogic, LogRetentionOptions } from '../log_retention'; import { Curation } from './curation'; import { Curations, CurationCreation, CurationSuggestion } from './views'; -import { CurationsSettingsLogic } from './views/curations_settings'; export const CurationsRouter: React.FC = () => { - // We need to loadCurationsSettings here so they are available across all views - - const { hasPlatinumLicense } = useValues(LicensingLogic); - - const { loadCurationsSettings, onSkipLoadingCurationsSettings } = - useActions(CurationsSettingsLogic); - - const { logRetention } = useValues(LogRetentionLogic); - const { fetchLogRetention } = useActions(LogRetentionLogic); - - const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; - - useEffect(() => { - if (hasPlatinumLicense) { - fetchLogRetention(); - } - }, [hasPlatinumLicense]); - - useEffect(() => { - if (logRetention) { - if (!analyticsDisabled) { - loadCurationsSettings(); - } else { - onSkipLoadingCurationsSettings(); - } - } - }, [logRetention]); - return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 2207555772b5bf..b0f4f03789af22 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -24,17 +24,15 @@ import { getCurationsBreadcrumbs } from '../utils'; import { CurationsHistory } from './curations_history/curations_history'; import { CurationsOverview } from './curations_overview'; -import { CurationsSettings, CurationsSettingsLogic } from './curations_settings'; +import { CurationsSettings } from './curations_settings'; export const Curations: React.FC = () => { - const { dataLoading: curationsDataLoading, meta, selectedPageTab } = useValues(CurationsLogic); + const { dataLoading, meta, selectedPageTab } = useValues(CurationsLogic); const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); const { engine: { search_relevance_suggestions_active: searchRelevanceSuggestionsActive }, } = useValues(EngineLogic); - const { dataLoading: curationsSettingsDataLoading } = useValues(CurationsSettingsLogic); - const suggestionsEnabled = searchRelevanceSuggestionsActive; const OVERVIEW_TAB = { @@ -82,8 +80,6 @@ export const Curations: React.FC = () => { loadCurations(); }, [meta.page.current]); - const isLoading = curationsSettingsDataLoading || curationsDataLoading; - return ( { {CREATE_NEW_CURATION_TITLE} , ], - tabs: isLoading ? undefined : pageTabs, + tabs: dataLoading ? undefined : pageTabs, }} - isLoading={isLoading} + isLoading={dataLoading} > {selectedPageTab === 'overview' && } {selectedPageTab === 'history' && } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx index 3b01d1e41c2718..4b4e11c31d4b88 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx @@ -17,6 +17,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiButtonEmpty, EuiCallOut, EuiSwitch } from '@elastic/eui'; +import { mountWithIntl } from '@kbn/test/jest'; + import { Loading } from '../../../../../shared/loading'; import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; import { DataPanel } from '../../../data_panel'; @@ -44,6 +46,8 @@ const MOCK_VALUES = { const MOCK_ACTIONS = { // CurationsSettingsLogic + loadCurationsSettings: jest.fn(), + onSkipLoadingCurationsSettings: jest.fn(), toggleCurationsEnabled: jest.fn(), toggleCurationsMode: jest.fn(), // LogRetentionLogic @@ -56,6 +60,14 @@ describe('CurationsSettings', () => { setMockActions(MOCK_ACTIONS); }); + it('loads curations and log retention settings on load', () => { + setMockValues(MOCK_VALUES); + mountWithIntl(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalled(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); + }); + it('contains a switch to toggle curations settings', () => { let wrapper: ShallowWrapper; @@ -154,6 +166,50 @@ describe('CurationsSettings', () => { expect(wrapper.is(Loading)).toBe(true); }); + describe('loading curation settings based on log retention', () => { + it('loads curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('skips loading curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: false, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('takes no action if log retention has not yet been loaded', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: null, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); + }); + }); + describe('when the user has no platinum license', () => { beforeEach(() => { setMockValues({ @@ -162,6 +218,11 @@ describe('CurationsSettings', () => { }); }); + it('it does not fetch log retention', () => { + shallow(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); + }); + it('shows a CTA to upgrade your license when the user when the user', () => { const wrapper = shallow(); expect(wrapper.is(DataPanel)).toBe(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx index a5d4a33d8b870b..de669298b11d95 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; @@ -43,12 +43,34 @@ export const CurationsSettings: React.FC = () => { curationsSettings: { enabled, mode }, dataLoading, } = useValues(CurationsSettingsLogic); - const { toggleCurationsEnabled, toggleCurationsMode } = useActions(CurationsSettingsLogic); + const { + loadCurationsSettings, + onSkipLoadingCurationsSettings, + toggleCurationsEnabled, + toggleCurationsMode, + } = useActions(CurationsSettingsLogic); const { isLogRetentionUpdating, logRetention } = useValues(LogRetentionLogic); + const { fetchLogRetention } = useActions(LogRetentionLogic); const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; + useEffect(() => { + if (hasPlatinumLicense) { + fetchLogRetention(); + } + }, [hasPlatinumLicense]); + + useEffect(() => { + if (logRetention) { + if (!analyticsDisabled) { + loadCurationsSettings(); + } else { + onSkipLoadingCurationsSettings(); + } + } + }, [logRetention]); + if (!hasPlatinumLicense) return ( { @@ -49,12 +49,12 @@ describe('PrecisionSlider', () => { expect(wrapper.find('[data-test-subj="PrecisionRange"]').prop('value')).toEqual(2); }); - it('calls updatePrecision on change', () => { + it('updates the precision on change', () => { wrapper .find('[data-test-subj="PrecisionRange"]') .simulate('change', { target: { value: 10 } }); - expect(MOCK_ACTIONS.updatePrecision).toHaveBeenCalledWith(10); + expect(MOCK_ACTIONS.setPrecision).toHaveBeenCalledWith(10); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx index 6fcfb28cb9e376..8e7a59c290ce2e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/components/precision_slider/precision_slider.tsx @@ -33,7 +33,7 @@ export const PrecisionSlider: React.FC = () => { searchSettings: { precision }, } = useValues(RelevanceTuningLogic); - const { updatePrecision } = useActions(RelevanceTuningLogic); + const { setPrecision } = useActions(RelevanceTuningLogic); const stepDescription = STEP_DESCRIPTIONS[precision]; @@ -102,7 +102,7 @@ export const PrecisionSlider: React.FC = () => { data-test-subj="PrecisionRange" value={precision} onChange={(e) => { - updatePrecision(parseInt((e.target as HTMLInputElement).value, 10)); + setPrecision(parseInt((e.target as HTMLInputElement).value, 10)); }} min={1} max={11} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts index 7590633b7afef4..193c5dbe8ac24d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.test.ts @@ -228,10 +228,10 @@ describe('RelevanceTuningLogic', () => { }); }); - describe('updatePrecision', () => { + describe('setPrecision', () => { it('should set precision inside search settings and set unsavedChanges to true', () => { mount(); - RelevanceTuningLogic.actions.updatePrecision(9); + RelevanceTuningLogic.actions.setPrecision(9); expect(RelevanceTuningLogic.values).toEqual({ ...DEFAULT_VALUES, @@ -1007,15 +1007,24 @@ describe('RelevanceTuningLogic', () => { }); }); - describe('updateSearchValue', () => { - it('should update the query then update search results', () => { + describe('setSearchQuery', () => { + it('shoulds update search results', () => { + mount(); + jest.spyOn(RelevanceTuningLogic.actions, 'getSearchResults'); + + RelevanceTuningLogic.actions.setSearchQuery('foo'); + + expect(RelevanceTuningLogic.actions.getSearchResults).toHaveBeenCalled(); + }); + }); + + describe('setPrecision', () => { + it('shoulds update search results', () => { mount(); - jest.spyOn(RelevanceTuningLogic.actions, 'setSearchQuery'); jest.spyOn(RelevanceTuningLogic.actions, 'getSearchResults'); - RelevanceTuningLogic.actions.updateSearchValue('foo'); + RelevanceTuningLogic.actions.setPrecision(9); - expect(RelevanceTuningLogic.actions.setSearchQuery).toHaveBeenCalledWith('foo'); expect(RelevanceTuningLogic.actions.getSearchResults).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts index 02df792f515a29..c94de74b5ab22e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_logic.ts @@ -88,8 +88,7 @@ interface RelevanceTuningActions { optionType: keyof Pick; value: string; }; - updatePrecision(precision: number): { precision: number }; - updateSearchValue(query: string): string; + setPrecision(precision: number): { precision: number }; } interface RelevanceTuningValues { @@ -144,8 +143,7 @@ export const RelevanceTuningLogic = kea< optionType, value, }), - updatePrecision: (precision) => ({ precision }), - updateSearchValue: (query) => query, + setPrecision: (precision) => ({ precision }), }), reducers: () => ({ searchSettings: [ @@ -158,7 +156,7 @@ export const RelevanceTuningLogic = kea< onInitializeRelevanceTuning: (_, { searchSettings }) => searchSettings, setSearchSettings: (_, { searchSettings }) => searchSettings, setSearchSettingsResponse: (_, { searchSettings }) => searchSettings, - updatePrecision: (currentSearchSettings, { precision }) => ({ + setPrecision: (currentSearchSettings, { precision }) => ({ ...currentSearchSettings, precision, }), @@ -191,7 +189,7 @@ export const RelevanceTuningLogic = kea< unsavedChanges: [ false, { - updatePrecision: () => true, + setPrecision: () => true, setSearchSettings: () => true, setSearchSettingsResponse: () => false, }, @@ -268,7 +266,11 @@ export const RelevanceTuningLogic = kea< const { engineName } = EngineLogic.values; const { http } = HttpLogic.values; - const { search_fields: searchFields, boosts } = removeBoostStateProps(values.searchSettings); + const { + search_fields: searchFields, + boosts, + precision, + } = removeBoostStateProps(values.searchSettings); const url = `/internal/app_search/engines/${engineName}/search`; actions.setResultsLoading(true); @@ -283,6 +285,7 @@ export const RelevanceTuningLogic = kea< body: JSON.stringify({ boosts: isEmpty(filteredBoosts) ? undefined : filteredBoosts, search_fields: isEmpty(searchFields) ? undefined : searchFields, + precision, }), }); @@ -472,8 +475,10 @@ export const RelevanceTuningLogic = kea< }, }); }, - updateSearchValue: (query) => { - actions.setSearchQuery(query); + setSearchQuery: () => { + actions.getSearchResults(); + }, + setPrecision: () => { actions.getSearchResults(); }, }), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx index b39faa60294367..32325c13efdaea 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.test.tsx @@ -22,7 +22,7 @@ describe('RelevanceTuningPreview', () => { const result3 = { id: { raw: 3 } }; const actions = { - updateSearchValue: jest.fn(), + setSearchQuery: jest.fn(), }; const values = { @@ -79,7 +79,7 @@ describe('RelevanceTuningPreview', () => { wrapper.find(EuiFieldSearch).simulate('change', { target: { value: 'some search text' } }); - expect(actions.updateSearchValue).toHaveBeenCalledWith('some search text'); + expect(actions.setSearchQuery).toHaveBeenCalledWith('some search text'); }); it('will show user a prompt to enter a query if they have not entered one', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx index 4f3b20b419e802..3a36a3df2b4722 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/relevance_tuning_preview.tsx @@ -44,7 +44,7 @@ const noResultsCallout = ( ); export const RelevanceTuningPreview: React.FC = () => { - const { updateSearchValue } = useActions(RelevanceTuningLogic); + const { setSearchQuery } = useActions(RelevanceTuningLogic); const { searchResults, schema } = useValues(RelevanceTuningLogic); const { engineName, isMetaEngine } = useValues(EngineLogic); @@ -59,7 +59,7 @@ export const RelevanceTuningPreview: React.FC = () => { updateSearchValue(e.target.value)} + onChange={(e) => setSearchQuery(e.target.value)} placeholder={i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.relevanceTuning.preview.searchPlaceholder', { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts index be953f973ebf83..0edace74d0cb95 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/relevance_tuning/utils.ts @@ -16,7 +16,7 @@ export const filterIfTerm = (array: string[], filterTerm: string): string[] => { return filterTerm === '' ? array : array.filter((item) => item.includes(filterTerm)); }; -export const removeBoostStateProps = (searchSettings: SearchSettings) => { +export const removeBoostStateProps = (searchSettings: SearchSettings): SearchSettings => { const updatedSettings = cloneDeep(searchSettings); const { boosts } = updatedSettings; const keys = Object.keys(boosts); diff --git a/x-pack/plugins/file_upload/kibana.json b/x-pack/plugins/file_upload/kibana.json index e69c5e34bc09bf..f70bc6377972a1 100644 --- a/x-pack/plugins/file_upload/kibana.json +++ b/x-pack/plugins/file_upload/kibana.json @@ -1,6 +1,6 @@ { "id": "fileUpload", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": true, "ui": true, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx index 9c9027fb94ac52..fa9b9a7ed190e0 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx @@ -22,6 +22,8 @@ import { EuiFlexItem, } from '@elastic/eui'; +import { useStartServices } from '../../../hooks'; + export type IntegrationPreferenceType = 'recommended' | 'beats' | 'agent'; interface Option { @@ -34,23 +36,6 @@ export interface Props { onChange: (type: IntegrationPreferenceType) => void; } -const link = ( - - - -); - -const title = ( - -); - const recommendedTooltip = ( { const [idSelected, setIdSelected] = React.useState(initialType); + + const { docLinks } = useStartServices(); + + const link = ( + + + + ); + + const title = ( + + ); + const radios = options.map((option) => ({ id: option.type, value: option.type, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 6e3eba19c52e3d..c8c6f493568109 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -271,6 +271,7 @@ export function Detail() { { path: pagePathGetters.integration_details_policies({ pkgkey, + ...(integration ? { integration } : {}), })[1], }, ]; @@ -289,6 +290,7 @@ export function Detail() { { path: pagePathGetters.integration_details_overview({ pkgkey, + ...(integration ? { integration } : {}), })[1], }, ], diff --git a/x-pack/plugins/fleet/server/services/agents/helpers.ts b/x-pack/plugins/fleet/server/services/agents/helpers.ts index 609d5ba6c83a05..a4a7803d35e2cb 100644 --- a/x-pack/plugins/fleet/server/services/agents/helpers.ts +++ b/x-pack/plugins/fleet/server/services/agents/helpers.ts @@ -42,5 +42,9 @@ export function agentSOAttributesToFleetServerAgentDoc( doc.policy_revision_idx = policyRevison; } + if (!doc.updated_at) { + doc.updated_at = new Date().toISOString(); + } + return doc; } diff --git a/x-pack/plugins/global_search_bar/kibana.json b/x-pack/plugins/global_search_bar/kibana.json index 5c0a9999b8e3a5..94b8aba2dd7f9f 100644 --- a/x-pack/plugins/global_search_bar/kibana.json +++ b/x-pack/plugins/global_search_bar/kibana.json @@ -4,7 +4,7 @@ "name": "Kibana Core", "githubTeam": "kibana-core" }, - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": false, "ui": true, diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index 410a5e2c160d6f..03729c706df25d 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -1,6 +1,6 @@ { "id": "graph", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": true, "ui": true, @@ -29,4 +29,4 @@ "name": "Data Discovery", "githubTeam": "kibana-data-discovery" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/grokdebugger/kibana.json b/x-pack/plugins/grokdebugger/kibana.json index 692aa16329d549..f8cb75f7d10b66 100644 --- a/x-pack/plugins/grokdebugger/kibana.json +++ b/x-pack/plugins/grokdebugger/kibana.json @@ -1,6 +1,6 @@ { "id": "grokdebugger", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "owner": { "name": "Stack Management", diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts index be4f99103b319e..4c553cac02303b 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/form_validation/timing.test.ts @@ -12,7 +12,8 @@ import { PhaseWithTiming } from '../../../../common/types'; import { setupEnvironment } from '../../helpers'; import { setupValidationTestBed, ValidationTestBed } from './validation.helpers'; -describe(' timing validation', () => { +// FLAKY: https://github.com/elastic/kibana/issues/115307 +describe.skip(' timing validation', () => { let testBed: ValidationTestBed; let actions: ValidationTestBed['actions']; const { server, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx index d6d030c3ec7339..61ce87860d8972 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/policy_list/components/policy_table.tsx @@ -191,7 +191,9 @@ export const PolicyTable: React.FunctionComponent = ({ policies }) => { direction: 'asc', }, }} - search={{ box: { incremental: true } }} + search={{ + box: { incremental: true, 'data-test-subj': 'ilmSearchBar' }, + }} tableLayout="auto" items={policies} columns={columns} diff --git a/x-pack/plugins/index_management/common/constants/plugin.ts b/x-pack/plugins/index_management/common/constants/plugin.ts index ad57398000426b..060d42ca26b023 100644 --- a/x-pack/plugins/index_management/common/constants/plugin.ts +++ b/x-pack/plugins/index_management/common/constants/plugin.ts @@ -22,4 +22,4 @@ export const PLUGIN = { // "PluginInitializerContext.env.packageInfo.version". In some cases it is not possible // to dynamically inject that version without a huge refactor on the code base. // We will then keep this single constant to declare on which major branch we are. -export const MAJOR_VERSION = '8.0.0'; +export const MAJOR_VERSION = '8.1.0'; diff --git a/x-pack/plugins/infra/common/source_configuration/source_configuration.ts b/x-pack/plugins/infra/common/source_configuration/source_configuration.ts index 436432e9f0caf6..257cccc86595cd 100644 --- a/x-pack/plugins/infra/common/source_configuration/source_configuration.ts +++ b/x-pack/plugins/infra/common/source_configuration/source_configuration.ts @@ -49,8 +49,6 @@ export const TimestampFromString = new rt.Type( export const sourceConfigurationConfigFilePropertiesRT = rt.type({ sources: rt.type({ default: rt.partial({ - logAlias: rt.string, // Cannot be deprecated until 8.0.0. Will be converted to an indexName reference. - metricAlias: rt.string, fields: rt.partial({ timestamp: rt.string, message: rt.array(rt.string), diff --git a/x-pack/plugins/infra/server/deprecations.ts b/x-pack/plugins/infra/server/deprecations.ts index 70131cd96d117c..4c2f5f6a9b3d14 100644 --- a/x-pack/plugins/infra/server/deprecations.ts +++ b/x-pack/plugins/infra/server/deprecations.ts @@ -179,8 +179,6 @@ export const configDeprecations: ConfigDeprecationProvider = ({ deprecate }) => return completeConfig; } ), - deprecate('sources.default.logAlias', '8.0.0'), - deprecate('sources.default.metricAlias', '8.0.0'), ]; export const getInfraDeprecationsFactory = diff --git a/x-pack/plugins/infra/server/lib/sources/sources.ts b/x-pack/plugins/infra/server/lib/sources/sources.ts index 45da4546ad3b82..4d2f0a8c4a1598 100644 --- a/x-pack/plugins/infra/server/lib/sources/sources.ts +++ b/x-pack/plugins/infra/server/lib/sources/sources.ts @@ -212,26 +212,7 @@ export class InfraSources { fold(constant({}), identity) ); - // NOTE: Legacy logAlias needs converting to a logIndices reference until we can remove - // config file sources in 8.0.0. - if (staticSourceConfiguration && staticSourceConfiguration.logAlias) { - const convertedStaticSourceConfiguration: InfraStaticSourceConfiguration & { - logAlias?: string; - } = { - ...staticSourceConfiguration, - logIndices: { - type: 'index_name', - indexName: staticSourceConfiguration.logAlias, - }, - }; - delete convertedStaticSourceConfiguration.logAlias; - return mergeSourceConfiguration( - defaultSourceConfiguration, - convertedStaticSourceConfiguration - ); - } else { - return mergeSourceConfiguration(defaultSourceConfiguration, staticSourceConfiguration); - } + return mergeSourceConfiguration(defaultSourceConfiguration, staticSourceConfiguration); } private async getSavedSourceConfiguration( diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index bf9c5a152058e4..247888fc2ae70b 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -51,8 +51,6 @@ export const config: PluginConfigDescriptor = { schema.object({ default: schema.maybe( schema.object({ - logAlias: schema.maybe(schema.string()), // NOTE / TODO: Should be deprecated in 8.0.0 - metricAlias: schema.maybe(schema.string()), fields: schema.maybe( schema.object({ timestamp: schema.maybe(schema.string()), diff --git a/x-pack/plugins/ingest_pipelines/kibana.json b/x-pack/plugins/ingest_pipelines/kibana.json index 800d92b5c97488..889559826f1f1d 100644 --- a/x-pack/plugins/ingest_pipelines/kibana.json +++ b/x-pack/plugins/ingest_pipelines/kibana.json @@ -1,6 +1,6 @@ { "id": "ingestPipelines", - "version": "8.0.0", + "version": "8.1.0", "server": true, "ui": true, "owner": { diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap index b058c42d8b4d10..6601167e1f83a6 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap @@ -67,6 +67,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` } } tickFormat={[Function]} + timeAxisLayerCount={0} title="c" /> { onClickValue, onSelectRange, syncColors: false, + useLegacyTimeAxis: false, }; }); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index a3022e830c8615..80e4a8d4dc8f70 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -31,6 +31,8 @@ import { LabelOverflowConstraint, DisplayValueStyle, RecursivePartial, + AxisStyle, + GridLineStyle, ScaleType, } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; @@ -90,6 +92,7 @@ export type XYChartRenderProps = XYChartProps & { paletteService: PaletteRegistry; formatFactory: FormatFactory; timeZone: string; + useLegacyTimeAxis: boolean; minInterval: number | undefined; interactive?: boolean; onClickValue: (data: LensFilterEvent['data']) => void; @@ -130,6 +133,7 @@ export const getXyChartRenderer = (dependencies: { chartsActiveCursorService: ChartsPluginStart['activeCursor']; paletteService: PaletteRegistry; timeZone: string; + useLegacyTimeAxis: boolean; }): ExpressionRenderDefinition => ({ name: 'lens_xy_chart_renderer', displayName: 'XY chart', @@ -160,6 +164,7 @@ export const getXyChartRenderer = (dependencies: { chartsThemeService={dependencies.chartsThemeService} paletteService={dependencies.paletteService} timeZone={dependencies.timeZone} + useLegacyTimeAxis={dependencies.useLegacyTimeAxis} minInterval={calculateMinInterval(config)} interactive={handlers.isInteractive()} onClickValue={onClickValue} @@ -235,6 +240,7 @@ export function XYChart({ onSelectRange, interactive = true, syncColors, + useLegacyTimeAxis, }: XYChartRenderProps) { const { legend, @@ -320,15 +326,15 @@ export function XYChart({ filteredBarLayers.some((layer) => layer.accessors.length > 1) || filteredBarLayers.some((layer) => layer.splitAccessor); - const isTimeViz = data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time'); + const isTimeViz = Boolean(data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time')); const isHistogramViz = filteredLayers.every((l) => l.isHistogram); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( filteredLayers, data, minInterval, - Boolean(isTimeViz), - Boolean(isHistogramViz) + isTimeViz, + isHistogramViz ); const yAxesMap = { @@ -555,6 +561,72 @@ export function XYChart({ floatingColumns: legend?.floatingColumns ?? 1, } as LegendPositionConfig; + // todo be moved in the chart plugin + const shouldUseNewTimeAxis = isTimeViz && !useLegacyTimeAxis && !shouldRotate; + + const gridLineStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 2, + }; + const xAxisStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + tickLabel: { + visible: Boolean(tickLabelsVisibilitySettings?.x), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: + referenceLinePaddings.bottom != null ? { inner: referenceLinePaddings.bottom } : 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(tickLabelsVisibilitySettings?.x), + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + } + : { + tickLabel: { + visible: tickLabelsVisibilitySettings?.x, + rotation: labelsOrientation?.x, + padding: + referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + }; return ( safeXAccessorLabelRenderer(d.value), }} - allowBrushingLastHistogramBin={Boolean(isTimeViz)} + allowBrushingLastHistogramBin={isTimeViz} rotation={shouldRotate ? 90 : 0} xDomain={xDomain} onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} @@ -618,29 +690,11 @@ export function XYChart({ id="x" position={shouldRotate ? Position.Left : Position.Bottom} title={xTitle} - gridLine={{ - visible: gridlinesVisibilitySettings?.x, - strokeWidth: 2, - }} + gridLine={gridLineStyle} hide={filteredLayers[0].hide || !filteredLayers[0].xAccessor} tickFormat={(d) => safeXAccessorLabelRenderer(d)} - style={{ - tickLabel: { - visible: tickLabelsVisibilitySettings?.x, - rotation: labelsOrientation?.x, - padding: - referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - axisTitle: { - visible: axisTitlesVisibilitySettings.x, - padding: - !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - }} + style={xAxisStyle} + timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} /> {yAxesConfiguration.map((axis) => { diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index f9d48ffaaae37b..d3d461e8f8741e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -12,6 +12,7 @@ import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public import type { LensPluginStartDependencies } from '../plugin'; import { getTimeZone } from '../utils'; import type { FormatFactory } from '../../common'; +import { LEGACY_TIME_AXIS } from '../../../../../src/plugins/charts/common'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; @@ -37,6 +38,7 @@ export class XyVisualization { chartsActiveCursorService: charts.activeCursor, paletteService: palettes, timeZone: getTimeZone(core.uiSettings), + useLegacyTimeAxis: core.uiSettings.get(LEGACY_TIME_AXIS), }) ); return getXyVisualization({ paletteService: palettes, fieldFormats }); diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index 285c4043e46c78..e1b5d3e8190b08 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -27,6 +27,7 @@ export type AbstractSourceDescriptor = { export type EMSTMSSourceDescriptor = AbstractSourceDescriptor & { // id: EMS TMS layer id. Used when !isAutoSelect isAutoSelect: boolean; + lightModeDefault: string; }; export type EMSFileSourceDescriptor = AbstractSourceDescriptor & { diff --git a/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.test.ts b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.test.ts new file mode 100644 index 00000000000000..5aab4e24c8ba66 --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.test.ts @@ -0,0 +1,58 @@ +/* + * 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 { setEmsTmsDefaultModes } from './set_ems_tms_default_modes'; + +describe('setEmsTmsDefaultModes', () => { + test('Should handle missing layerListJSON attribute', () => { + const attributes = { + title: 'my map', + }; + expect(setEmsTmsDefaultModes({ attributes })).toEqual({ + title: 'my map', + }); + }); + + test('Should add lightModeDefault to existing EMS_TMS source descriptors', () => { + const layerListJSON = JSON.stringify([ + { + sourceDescriptor: { + type: 'EMS_TMS', + }, + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(setEmsTmsDefaultModes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: '[{"sourceDescriptor":{"type":"EMS_TMS","lightModeDefault":"road_map"}}]', + }); + }); + + // test edge case where sample data maps set lightModeDefault but still run migration + test('Should not change lightModeDefault if provided', () => { + const layerListJSON = JSON.stringify([ + { + sourceDescriptor: { + type: 'EMS_TMS', + lightModeDefault: 'road_map_desaturated', + }, + }, + ]); + const attributes = { + title: 'my map', + layerListJSON, + }; + expect(setEmsTmsDefaultModes({ attributes })).toEqual({ + title: 'my map', + layerListJSON: + '[{"sourceDescriptor":{"type":"EMS_TMS","lightModeDefault":"road_map_desaturated"}}]', + }); + }); +}); diff --git a/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.ts b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.ts new file mode 100644 index 00000000000000..94dbd8741add7f --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/set_ems_tms_default_modes.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 { SOURCE_TYPES } from '../constants'; +import { LayerDescriptor, EMSTMSSourceDescriptor } from '../descriptor_types'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; + +// LightModeDefault added to EMSTMSSourceDescriptor in 8.0.0 +// to avoid changing auto selected light mode tiles for maps created < 8.0.0 +// < 8.0.0 did not specify defaults and used bright for light mode +// > 8.0.0 changed default light mode from bright to desaturated +export function setEmsTmsDefaultModes({ + attributes, +}: { + attributes: MapSavedObjectAttributes; +}): MapSavedObjectAttributes { + if (!attributes || !attributes.layerListJSON) { + return attributes; + } + + const layerList: LayerDescriptor[] = JSON.parse(attributes.layerListJSON); + layerList.forEach((layerDescriptor: LayerDescriptor) => { + if (layerDescriptor.sourceDescriptor?.type === SOURCE_TYPES.EMS_TMS) { + const sourceDescriptor = layerDescriptor.sourceDescriptor as EMSTMSSourceDescriptor; + // auto select bright tiles for EMS_TMS layers created before 8.0.0 + if (!sourceDescriptor.lightModeDefault) { + sourceDescriptor.lightModeDefault = 'road_map'; + } + } + }); + + return { + ...attributes, + layerListJSON: JSON.stringify(layerList), + }; +} diff --git a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts index 59ff7e5e6616be..cf3d8705380043 100644 --- a/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/create_basemap_layer_descriptor.test.ts @@ -85,6 +85,7 @@ describe('EMS is enabled', () => { source: undefined, sourceDescriptor: { isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', type: 'EMS_TMS', }, style: { type: 'TILE' }, diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js index fec10501209602..2acb0578f6152e 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.js @@ -43,7 +43,11 @@ export class EMSTMSSource extends AbstractTMSSource { type: SOURCE_TYPES.EMS_TMS, id: descriptor.id, isAutoSelect: - typeof descriptor.isAutoSelect !== 'undefined' ? !!descriptor.isAutoSelect : false, + typeof descriptor.isAutoSelect !== 'undefined' ? descriptor.isAutoSelect : false, + lightModeDefault: + typeof descriptor.lightModeDefault !== 'undefined' + ? descriptor.lightModeDefault + : getEmsTileLayerId().desaturated, }; } @@ -144,12 +148,11 @@ export class EMSTMSSource extends AbstractTMSSource { } getTileLayerId() { - if (!this._descriptor.isAutoSelect) { + if (!this._descriptor.isAutoSelect && this._descriptor.id) { return this._descriptor.id; } - const emsTileLayerId = getEmsTileLayerId(); - return getIsDarkMode() ? emsTileLayerId.dark : emsTileLayerId.bright; + return getIsDarkMode() ? getEmsTileLayerId().dark : this._descriptor.lightModeDefault; } async getLicensedFeatures() { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js index 8998e895f6541f..833e4b0fff95ec 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/ems_tms_source.test.js @@ -5,6 +5,18 @@ * 2.0. */ +jest.mock('../../../kibana_services', () => { + return { + getEmsTileLayerId: () => { + return { + bright: 'road_map', + desaturated: 'road_map_desaturated', + dark: 'dark_map', + }; + }, + }; +}); + jest.mock('../../../util', () => { return { getEmsTmsServices: () => { diff --git a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx index e4a6fed934b8d4..60c047b13bb7ad 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_tms_source/tile_service_select.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import { getEmsTmsServices } from '../../../util'; import { getEmsUnavailableMessage } from '../../../components/ems_unavailable_message'; -export const AUTO_SELECT = 'auto_select'; +const AUTO_SELECT = 'auto_select'; export interface EmsTmsSourceConfig { id: string | null; diff --git a/x-pack/plugins/maps/server/embeddable_migrations.ts b/x-pack/plugins/maps/server/embeddable_migrations.ts index 2a53198d8d247a..a49e776d4fe02f 100644 --- a/x-pack/plugins/maps/server/embeddable_migrations.ts +++ b/x-pack/plugins/maps/server/embeddable_migrations.ts @@ -8,12 +8,13 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { MapSavedObjectAttributes } from '../common/map_saved_object_type'; import { moveAttribution } from '../common/migrations/move_attribution'; +import { setEmsTmsDefaultModes } from '../common/migrations/set_ems_tms_default_modes'; /* * Embeddables such as Maps, Lens, and Visualize can be embedded by value or by reference on a dashboard. * To ensure that any migrations (>7.12) are run correctly in both cases, * the migration function must be registered as both a saved object migration and an embeddable migration - + * * This is the embeddable migration registry. */ export const embeddableMigrations = { @@ -23,4 +24,10 @@ export const embeddableMigrations = { attributes: moveAttribution(state as { attributes: MapSavedObjectAttributes }), } as SerializableRecord; }, + '8.0.0': (state: SerializableRecord) => { + return { + ...state, + attributes: setEmsTmsDefaultModes(state as { attributes: MapSavedObjectAttributes }), + } as SerializableRecord; + }, }; diff --git a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js b/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js index a0ac4db734bd7e..e778b9e4162302 100644 --- a/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/ecommerce_saved_objects.js @@ -15,6 +15,7 @@ const layerList = [ sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', }, visible: true, style: {}, diff --git a/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js b/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js index 4e9915623d7c7d..645eb0a90e5601 100644 --- a/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/flights_saved_objects.js @@ -14,6 +14,7 @@ const layerList = [ sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', }, visible: true, style: {}, diff --git a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js index 984b9de7ac2b6c..5cc460160a676e 100644 --- a/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js +++ b/x-pack/plugins/maps/server/sample_data/web_logs_saved_objects.js @@ -15,6 +15,7 @@ const layerList = [ sourceDescriptor: { type: 'EMS_TMS', isAutoSelect: true, + lightModeDefault: 'road_map_desaturated', }, visible: true, style: {}, diff --git a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js index 8866ebb6b3de39..5fc15e89297143 100644 --- a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js +++ b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js @@ -17,12 +17,13 @@ import { removeBoundsFromSavedObject } from '../../common/migrations/remove_boun import { setDefaultAutoFitToBounds } from '../../common/migrations/set_default_auto_fit_to_bounds'; import { addTypeToTermJoin } from '../../common/migrations/add_type_to_termjoin'; import { moveAttribution } from '../../common/migrations/move_attribution'; +import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes'; /* * Embeddables such as Maps, Lens, and Visualize can be embedded by value or by reference on a dashboard. * To ensure that any migrations (>7.12) are run correctly in both cases, * the migration function must be registered as both a saved object migration and an embeddable migration - + * * This is the saved object migration registry. */ export const savedObjectMigrations = { @@ -104,6 +105,14 @@ export const savedObjectMigrations = { '7.14.0': (doc) => { const attributes = moveAttribution(doc); + return { + ...doc, + attributes, + }; + }, + '8.0.0': (doc) => { + const attributes = setEmsTmsDefaultModes(doc); + return { ...doc, attributes, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json index 20343755f7f0f1..1b7d353d9f3038 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json @@ -22,7 +22,7 @@ }, "model_memory_limit": "50mb", "create_time": 1568974998023, - "version": "8.0.0" + "version": "8.1.0" }, "id": "fq_outlier_1222", "checkpointing": {}, diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js index 1854982c8db0bf..7f9fcc7bc55170 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js @@ -78,6 +78,12 @@ function getColumns(viewForecast) { // TODO - add in ml-info-icon to the h3 element, // then remove tooltip and inline style. export function ForecastsList({ forecasts, viewForecast }) { + const getRowProps = (item) => { + return { + 'data-test-subj': `mlForecastsListRow row-${item.rowId}`, + }; + }; + return (

); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 87131583e44eb4..cad5bb68fb62b7 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -547,9 +547,18 @@ class TimeseriesChartIntl extends Component { // Create the path elements for the forecast value line and bounds area. if (contextForecastData) { - fcsGroup.append('path').attr('class', 'area forecast'); - fcsGroup.append('path').attr('class', 'values-line forecast'); - fcsGroup.append('g').attr('class', 'focus-chart-markers forecast'); + fcsGroup + .append('path') + .attr('class', 'area forecast') + .attr('data-test-subj', 'mlForecastArea'); + fcsGroup + .append('path') + .attr('class', 'values-line forecast') + .attr('data-test-subj', 'mlForecastValuesline'); + fcsGroup + .append('g') + .attr('class', 'focus-chart-markers forecast') + .attr('data-test-subj', 'mlForecastMarkers'); } fcsGroup diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 9b8770350909ea..e4d7fc457de0b7 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -1170,9 +1170,13 @@ export class TimeSeriesExplorer extends React.Component { + {i18n.translate('xpack.ml.timeSeriesExplorer.showForecastLabel', { + defaultMessage: 'show forecast', + })} + + } checked={showForecast} onChange={this.toggleShowForecastHandler} /> diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx index 496e7a10f9c447..eca18f0eb0dd40 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/report_metric_options.tsx @@ -13,6 +13,8 @@ import { EuiListGroup, EuiListGroupItem, EuiBadge, + EuiText, + EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -33,7 +35,7 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { const [showOptions, setShowOptions] = useState(false); const metricOptions = seriesConfig?.metricOptions; - const { indexPatterns } = useAppIndexPatternContext(); + const { indexPatterns, loading } = useAppIndexPatternContext(); const onChange = (value?: string) => { setSeries(seriesId, { @@ -78,6 +80,10 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { }; }); + if (!indexPattern && !loading) { + return {NO_DATA_AVAILABLE}; + } + return ( <> {!series.selectedMetricField && ( @@ -88,6 +94,7 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { onClick={() => setShowOptions((prevState) => !prevState)} fill size="s" + isLoading={!indexPattern && loading} > {SELECT_REPORT_METRIC_LABEL} @@ -107,19 +114,23 @@ export function ReportMetricOptions({ seriesId, series, seriesConfig }: Props) { )} - {series.selectedMetricField && ( - onChange(undefined)} - iconOnClickAriaLabel={REMOVE_REPORT_METRIC_LABEL} - > - { - seriesConfig?.metricOptions?.find((option) => option.id === series.selectedMetricField) - ?.label - } - - )} + {series.selectedMetricField && + (indexPattern && !loading ? ( + onChange(undefined)} + iconOnClickAriaLabel={REMOVE_REPORT_METRIC_LABEL} + > + { + seriesConfig?.metricOptions?.find( + (option) => option.id === series.selectedMetricField + )?.label + } + + ) : ( + + ))} ); } @@ -137,3 +148,7 @@ const REMOVE_REPORT_METRIC_LABEL = i18n.translate( defaultMessage: 'Remove report metric', } ); + +const NO_DATA_AVAILABLE = i18n.translate('xpack.observability.expView.seriesEditor.noData', { + defaultMessage: 'No data available', +}); diff --git a/x-pack/plugins/painless_lab/kibana.json b/x-pack/plugins/painless_lab/kibana.json index 7c71d4bdb4b761..1f59bf30bb761f 100644 --- a/x-pack/plugins/painless_lab/kibana.json +++ b/x-pack/plugins/painless_lab/kibana.json @@ -1,6 +1,6 @@ { "id": "painlessLab", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "owner": { "name": "Stack Management", diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index 5a36924b26433c..072d6d437b8b99 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -20,7 +20,7 @@ export const PLUGIN = { }, }; -export const MAJOR_VERSION = '8.0.0'; +export const MAJOR_VERSION = '8.1.0'; export const API_BASE_PATH = '/api/remote_clusters'; diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts index 7677f37702f0d8..7b4cc2008a6769 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts @@ -28,7 +28,8 @@ type SetupServerReturn = UnwrapPromise>; const devtoolMessage = 'DevTools listening on (ws://localhost:4000)'; const fontNotFoundMessage = 'Could not find the default font'; -describe('POST /diagnose/browser', () => { +// FLAKY: https://github.com/elastic/kibana/issues/89369 +describe.skip('POST /diagnose/browser', () => { jest.setTimeout(6000); const reportingSymbol = Symbol('reporting'); const mockLogger = createMockLevelLogger(); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts index 171d224cc32d35..10f556a11bf602 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/event_correlation_rule.spec.ts @@ -70,7 +70,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { ALERTS_URL } from '../../urls/navigation'; -describe('Detection rules, EQL', () => { +describe.skip('Detection rules, EQL', () => { const expectedUrls = getEqlRule().referenceUrls.join(''); const expectedFalsePositives = getEqlRule().falsePositivesExamples.join(''); const expectedTags = getEqlRule().tags.join(''); @@ -169,7 +169,7 @@ describe('Detection rules, EQL', () => { }); }); -describe('Detection rules, sequence EQL', () => { +describe.skip('Detection rules, sequence EQL', () => { const expectedNumberOfRules = 1; const expectedNumberOfSequenceAlerts = '1 alert'; diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts index 002aa0bbc2b1ea..cea290eeef17be 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_alert.spec.ts @@ -8,7 +8,7 @@ import { getException } from '../../objects/exception'; import { getNewRule } from '../../objects/rule'; -import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../screens/alerts'; +import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { RULE_STATUS } from '../../screens/create_new_rule'; import { @@ -31,13 +31,12 @@ import { removeException, waitForTheRuleToBeExecuted, } from '../../tasks/rule_details'; -import { refreshPage } from '../../tasks/security_header'; import { ALERTS_URL } from '../../urls/navigation'; import { cleanKibana } from '../../tasks/common'; -describe('From alert', () => { - const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1'; +describe.skip('From alert', () => { + const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1 alert'; beforeEach(() => { cleanKibana(); @@ -53,7 +52,6 @@ describe('From alert', () => { activatesRule(); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); cy.get(NUMBER_OF_ALERTS).should('have.text', NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS); @@ -64,36 +62,30 @@ describe('From alert', () => { esArchiverUnload('auditbeat_for_exceptions2'); }); - // TODO: Unskip the test when `https://github.com/elastic/kibana/issues/108244` it is fixed - it.skip('Creates an exception and deletes it', () => { + it('Creates an exception and deletes it', () => { addExceptionFromFirstAlert(); addsException(getException()); esArchiverLoad('auditbeat_for_exceptions2'); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToClosedAlerts(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); goToOpenedAlerts(); waitForTheRuleToBeExecuted(); - refreshPage(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToExceptionsTab(); removeException(); goToAlertsTab(); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS}`); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts index c4e1d882d1853d..4af6467e5d33c2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/exceptions/from_rule.spec.ts @@ -8,7 +8,7 @@ import { getException } from '../../objects/exception'; import { getNewRule } from '../../objects/rule'; -import { ALERTS_COUNT, NUMBER_OF_ALERTS } from '../../screens/alerts'; +import { ALERTS_COUNT, EMPTY_ALERT_TABLE, NUMBER_OF_ALERTS } from '../../screens/alerts'; import { RULE_STATUS } from '../../screens/create_new_rule'; import { @@ -35,7 +35,7 @@ import { refreshPage } from '../../tasks/security_header'; import { ALERTS_URL } from '../../urls/navigation'; import { cleanKibana } from '../../tasks/common'; -describe('From rule', () => { +describe.skip('From rule', () => { const NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS = '1'; beforeEach(() => { cleanKibana(); @@ -54,7 +54,7 @@ describe('From rule', () => { refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alert`); }); afterEach(() => { @@ -62,40 +62,32 @@ describe('From rule', () => { esArchiverUnload('auditbeat_for_exceptions2'); }); - // TODO: Unskip the test when `https://github.com/elastic/kibana/issues/108244` it is fixed - it.skip('Creates an exception and deletes it', () => { + it('Creates an exception and deletes it', () => { goToExceptionsTab(); addsExceptionFromRuleSettings(getException()); esArchiverLoad('auditbeat_for_exceptions2'); waitForTheRuleToBeExecuted(); goToAlertsTab(); - refreshPage(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToClosedAlerts(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alert`); goToOpenedAlerts(); waitForTheRuleToBeExecuted(); - refreshPage(); - cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', '0 alerts'); + cy.get(EMPTY_ALERT_TABLE).should('exist'); goToExceptionsTab(); removeException(); - refreshPage(); goToAlertsTab(); waitForTheRuleToBeExecuted(); waitForAlertsToPopulate(); - refreshPage(); cy.get(ALERTS_COUNT).should('exist'); - cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alerts`); + cy.get(NUMBER_OF_ALERTS).should('have.text', `${NUMBER_OF_AUDITBEAT_EXCEPTIONS_ALERTS} alert`); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index 01848f42078465..a46f65acaf971f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -34,6 +34,8 @@ export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="close-alert-status"]' export const CLOSED_ALERTS_FILTER_BTN = '[data-test-subj="closedAlerts"]'; +export const EMPTY_ALERT_TABLE = '[data-test-subj="tGridEmptyState"]'; + export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; export const ACKNOWLEDGED_ALERTS_FILTER_BTN = '[data-test-subj="acknowledgedAlerts"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index 067c9957189b9e..220c0fbdaa4cf4 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -34,7 +34,7 @@ import { } from '../screens/alerts_details'; export const addExceptionFromFirstAlert = () => { - cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click(); + cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true }); cy.get(ADD_EXCEPTION_BTN).click(); }; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts index 36f02d22487fc4..448cb215941deb 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts @@ -9,6 +9,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { useCurrentUser, useHttp } from '../../../lib/kibana'; import { appRoutesService, CheckPermissionsResponse } from '../../../../../../fleet/common'; import { useLicense } from '../../../hooks/use_license'; +import { Immutable } from '../../../../../common/endpoint/types'; export interface EndpointPrivileges { loading: boolean; @@ -30,7 +31,7 @@ export interface EndpointPrivileges { * **NOTE:** Consider using `usePrivileges().endpointPrivileges` instead of this hook in order * to keep API calls to a minimum. */ -export const useEndpointPrivileges = (): EndpointPrivileges => { +export const useEndpointPrivileges = (): Immutable => { const http = useHttp(); const user = useCurrentUser(); const isMounted = useRef(true); @@ -66,7 +67,7 @@ export const useEndpointPrivileges = (): EndpointPrivileges => { }, [user?.roles]); const privileges = useMemo(() => { - const privilegeList: EndpointPrivileges = { + const privilegeList: EndpointPrivileges = Object.freeze({ loading: !fleetCheckDone || !user, canAccessFleet, canAccessEndpointManagement: canAccessFleet && isSuperUser, @@ -75,7 +76,7 @@ export const useEndpointPrivileges = (): EndpointPrivileges => { // FIXME: Remove usages of the property below /** @deprecated */ isPlatinumPlus: isPlatinumPlusLicense, - }; + }); return privilegeList; }, [canAccessFleet, fleetCheckDone, isSuperUser, user, isPlatinumPlusLicense]); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index 6b5d6f03aca286..6acc48c55c6e3a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -10,12 +10,7 @@ import { i18n } from '@kbn/i18n'; import { useLocation } from 'react-router-dom'; import { EuiCallOut, EuiLoadingSpinner, EuiPageTemplate } from '@elastic/eui'; import { usePolicyDetailsSelector } from './policy_hooks'; -import { - policyDetails, - agentStatusSummary, - isLoading, - apiError, -} from '../store/policy_details/selectors'; +import { policyDetails, agentStatusSummary, apiError } from '../store/policy_details/selectors'; import { AgentsSummary } from './agents_summary'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { PolicyTabs } from './tabs'; @@ -39,7 +34,6 @@ export const PolicyDetails = React.memo(() => { const { getAppUrl } = useAppUrl(); // Store values - const loading = usePolicyDetailsSelector(isLoading); const policyApiError = usePolicyDetailsSelector(apiError); const policyItem = usePolicyDetailsSelector(policyDetails); const policyAgentStatusSummary = usePolicyDetailsSelector(agentStatusSummary); @@ -90,24 +84,24 @@ export const PolicyDetails = React.memo(() => { ); const pageBody: React.ReactNode = useMemo(() => { - if (loading) { + if (policyApiError) { return ( - + + {policyApiError?.message} + ); } - if (policyApiError) { + if (!policyItem) { return ( - - {policyApiError?.message} - + ); } @@ -118,7 +112,7 @@ export const PolicyDetails = React.memo(() => { } return ; - }, [isTrustedAppsByPolicyEnabled, loading, policyApiError]); + }, [isTrustedAppsByPolicyEnabled, policyApiError, policyItem]); return ( ; }; export interface PivotConfigDefinition { diff --git a/x-pack/plugins/transform/public/app/common/request.ts b/x-pack/plugins/transform/public/app/common/request.ts index 8f8341260bd7eb..184e3d31e89d21 100644 --- a/x-pack/plugins/transform/public/app/common/request.ts +++ b/x-pack/plugins/transform/public/app/common/request.ts @@ -242,6 +242,7 @@ export const getCreateTransformRequestBody = ( }, } : {}), + ...(transformDetailsState._meta ? { _meta: transformDetailsState._meta } : {}), // conditionally add additional settings ...getCreateTransformSettingsRequestBody(transformDetailsState), }); diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx b/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx index 9729cc8e62b1f9..7aae41cf2e769c 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx @@ -78,7 +78,8 @@ describe('Transform: useIndexData()', () => { }); }); -describe('Transform: with useIndexData()', () => { +// FLAKY: https://github.com/elastic/kibana/issues/109943 +describe.skip('Transform: with useIndexData()', () => { test('Minimal initialization, no cross cluster search warning.', async () => { // Arrange const indexPattern = { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts index 39b1a2de26f8ec..21e6bce204ec85 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts @@ -27,6 +27,7 @@ export interface StepDetailsExposedState { transformSettingsDocsPerSecond?: number; valid: boolean; indexPatternTimeField?: string | undefined; + _meta?: Record; } const defaultContinuousModeDelay = '60s'; @@ -94,6 +95,10 @@ export function applyTransformConfigToDetailsState( state.transformSettingsDocsPerSecond = transformConfig.settings.docs_per_second; } } + + if (transformConfig._meta) { + state._meta = transformConfig._meta; + } } return state; } diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index 416bad15d800a6..eda95013f60bd1 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -289,6 +289,7 @@ export const StepDetailsForm: FC = React.memo( touched: true, valid, indexPatternTimeField, + _meta: defaults._meta, }); // custom comparison /* eslint-disable react-hooks/exhaustive-deps */ diff --git a/x-pack/test/accessibility/apps/index_lifecycle_management.ts b/x-pack/test/accessibility/apps/index_lifecycle_management.ts index 35f4a8e1adea57..6cec8d1cb891ab 100644 --- a/x-pack/test/accessibility/apps/index_lifecycle_management.ts +++ b/x-pack/test/accessibility/apps/index_lifecycle_management.ts @@ -7,6 +7,7 @@ import { FtrProviderContext } from '../ftr_provider_context'; +const REPO_NAME = 'test'; const POLICY_NAME = 'ilm-a11y-test'; const POLICY_ALL_PHASES = { policy: { @@ -23,7 +24,7 @@ const POLICY_ALL_PHASES = { frozen: { actions: { searchable_snapshot: { - snapshot_repository: 'test', + snapshot_repository: REPO_NAME, }, }, }, @@ -46,7 +47,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esClient = getService('es'); const a11y = getService('a11y'); + const filterByPolicyName = async (policyName: string) => { + await testSubjects.setValue('ilmSearchBar', policyName); + }; + const findPolicyLinkInListView = async (policyName: string) => { + await filterByPolicyName(policyName); const links = await testSubjects.findAll('policyTablePolicyNameLink'); for (const link of links) { const name = await link.getVisibleText(); @@ -57,11 +63,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { throw new Error(`Could not find ${policyName} in policy table`); }; - // FLAKY - // https://github.com/elastic/kibana/issues/114541 - // https://github.com/elastic/kibana/issues/114542 - describe.skip('Index Lifecycle Management', async () => { + describe('Index Lifecycle Management', async () => { before(async () => { + await esClient.snapshot.createRepository({ + name: REPO_NAME, + body: { + type: 'fs', + settings: { + // use one of the values defined in path.repo in test/functional/config.js + location: '/tmp/', + }, + }, + verify: false, + }); await esClient.ilm.putLifecycle({ name: POLICY_NAME, body: POLICY_ALL_PHASES }); await esClient.indices.putIndexTemplate({ name: indexTemplateName, @@ -79,6 +93,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); after(async () => { + await esClient.snapshot.deleteRepository({ + name: REPO_NAME, + }); await esClient.ilm.deleteLifecycle({ name: POLICY_NAME }); await esClient.indices.deleteIndexTemplate({ name: indexTemplateName }); }); @@ -144,6 +161,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('Add policy to index template modal', async () => { + await filterByPolicyName(POLICY_NAME); const policyRow = await testSubjects.find(`policyTableRow-${POLICY_NAME}`); const addPolicyButton = await policyRow.findByTestSubject('addPolicyToTemplate'); @@ -157,6 +175,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('Delete policy modal', async () => { + await filterByPolicyName(POLICY_NAME); const policyRow = await testSubjects.find(`policyTableRow-${POLICY_NAME}`); const deleteButton = await policyRow.findByTestSubject('deletePolicy'); @@ -170,6 +189,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('Index templates flyout', async () => { + await filterByPolicyName(POLICY_NAME); const policyRow = await testSubjects.find(`policyTableRow-${POLICY_NAME}`); const actionsButton = await policyRow.findByTestSubject('viewIndexTemplates'); diff --git a/x-pack/test/api_integration/apis/security_solution/authentications.ts b/x-pack/test/api_integration/apis/security_solution/authentications.ts index 4ea8b8ab82e16f..8254ce034d2a5b 100644 --- a/x-pack/test/api_integration/apis/security_solution/authentications.ts +++ b/x-pack/test/api_integration/apis/security_solution/authentications.ts @@ -6,7 +6,10 @@ */ import expect from '@kbn/expect'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostAuthenticationsStrategyResponse, + HostsQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -22,16 +25,19 @@ const EDGE_LENGTH = 1; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('authentications', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts')); + before(async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); + + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts') + ); it('Make sure that we get Authentication data', async () => { - const { body: authentications } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const authentications = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.authentications, timerange: { interval: '12h', @@ -47,9 +53,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(authentications.edges.length).to.be(EDGE_LENGTH); expect(authentications.totalCount).to.be(TOTAL_COUNT); @@ -57,10 +63,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that pagination is working in Authentications query', async () => { - const { body: authentications } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const authentications = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.authentications, timerange: { interval: '12h', @@ -76,16 +81,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(authentications.edges.length).to.be(EDGE_LENGTH); expect(authentications.totalCount).to.be(TOTAL_COUNT); - expect(authentications.edges[0]!.node.lastSuccess!.source!.ip).to.eql([ + expect(authentications.edges[0].node.lastSuccess?.source?.ip).to.eql([ LAST_SUCCESS_SOURCE_IP, ]); - expect(authentications.edges[0]!.node.lastSuccess!.host!.name).to.eql([HOST_NAME]); + expect(authentications.edges[0].node.lastSuccess?.host?.name).to.eql([HOST_NAME]); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/events.ts b/x-pack/test/api_integration/apis/security_solution/events.ts index f6a668679b11d2..fef37e9939fcb3 100644 --- a/x-pack/test/api_integration/apis/security_solution/events.ts +++ b/x-pack/test/api_integration/apis/security_solution/events.ts @@ -11,13 +11,13 @@ import { JsonObject } from '@kbn/utility-types'; import { Direction, TimelineEventsQueries, + TimelineEventsAllStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; import { getDocValueFields, getFieldsToRequest, getFilterValue } from './utils'; const TO = '3000-01-01T00:00:00.000Z'; const FROM = '2000-01-01T00:00:00.000Z'; -const TEST_URL = '/internal/search/timelineSearchStrategy/'; // typical values that have to change after an update from "scripts/es_archiver" const DATA_COUNT = 7; const HOST_NAME = 'suricata-sensor-amsterdam'; @@ -30,6 +30,8 @@ const LIMITED_PAGE_SIZE = 2; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + const getPostBody = (): JsonObject => ({ defaultIndex: ['auditbeat-*'], docValueFields: getDocValueFields(), @@ -66,14 +68,14 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns Timeline data', async () => { - const resp = await supertest - .post(TEST_URL) - .set('kbn-xsrf', 'true') - .set('Content-Type', 'application/json') - .send(getPostBody()) - .expect(200); + const timeline = await bsearch.send({ + supertest, + options: { + ...getPostBody(), + }, + strategy: 'timelineSearchStrategy', + }); - const timeline = resp.body; expect(timeline.edges.length).to.be(EDGE_LENGTH); expect(timeline.edges[0].node.data.length).to.be(DATA_COUNT); expect(timeline.totalCount).to.be(TOTAL_COUNT); @@ -82,20 +84,17 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns paginated Timeline query', async () => { - const resp = await supertest - .post(TEST_URL) - .set('kbn-xsrf', 'true') - .set('Content-Type', 'application/json') - .send({ + const timeline = await bsearch.send({ + supertest, + options: { ...getPostBody(), pagination: { activePage: 0, querySize: LIMITED_PAGE_SIZE, }, - }) - .expect(200); - - const timeline = resp.body; + }, + strategy: 'timelineSearchStrategy', + }); expect(timeline.edges.length).to.be(LIMITED_PAGE_SIZE); expect(timeline.edges[0].node.data.length).to.be(DATA_COUNT); expect(timeline.totalCount).to.be(TOTAL_COUNT); diff --git a/x-pack/test/api_integration/apis/security_solution/host_details.ts b/x-pack/test/api_integration/apis/security_solution/host_details.ts index 114f60a21c4e35..d2de0f84a37698 100644 --- a/x-pack/test/api_integration/apis/security_solution/host_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/host_details.ts @@ -7,16 +7,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostDetailsStrategyResponse, + HostsQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Host Details', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -213,12 +221,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get HostDetails data', async () => { - const { - body: { hostDetails }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { hostDetails } = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.details, timerange: { interval: '12h', @@ -229,9 +234,9 @@ export default function ({ getService }: FtrProviderContext) { docValueFields: [], hostName: 'raspberrypi', inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hostDetails).to.eql(expectedResult.hostDetails); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/hosts.ts b/x-pack/test/api_integration/apis/security_solution/hosts.ts index 4df46002e9a13f..bb2969f85a98b8 100644 --- a/x-pack/test/api_integration/apis/security_solution/hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/hosts.ts @@ -10,6 +10,9 @@ import { HostsQueries, Direction, HostsFields, + HostsStrategyResponse, + HostDetailsStrategyResponse, + HostFirstLastSeenStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -26,17 +29,19 @@ const CURSOR_ID = '2ab45fc1c41e4c84bbd02202a7e5761f'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); - // Failing: See https://github.com/elastic/kibana/issues/104260 - describe.skip('hosts', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts')); + describe('hosts', () => { + before(async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); + + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts') + ); it('Make sure that we get Hosts Table data', async () => { - const { body: hosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const hosts = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.hosts, timerange: { interval: '12h', @@ -56,19 +61,18 @@ export default function ({ getService }: FtrProviderContext) { querySize: 1, }, inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hosts.edges.length).to.be(EDGE_LENGTH); expect(hosts.totalCount).to.be(TOTAL_COUNT); expect(hosts.pageInfo.fakeTotalCount).to.equal(3); }); it('Make sure that pagination is working in Hosts Table query', async () => { - const { body: hosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const hosts = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.hosts, timerange: { interval: '12h', @@ -88,16 +92,32 @@ export default function ({ getService }: FtrProviderContext) { querySize: 2, }, inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hosts.edges.length).to.be(EDGE_LENGTH); expect(hosts.totalCount).to.be(TOTAL_COUNT); - expect(hosts.edges[0]!.node.host!.os!.name).to.eql([HOST_NAME]); + expect(hosts.edges[0].node.host?.os?.name).to.eql([HOST_NAME]); }); it('Make sure that we get Host details data', async () => { - const expectedHostDetails = { + const { hostDetails } = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.details, + hostName: 'zeek-sensor-san-francisco', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(hostDetails).to.eql({ _id: 'zeek-sensor-san-francisco', host: { architecture: ['x86_64'], @@ -122,91 +142,66 @@ export default function ({ getService }: FtrProviderContext) { provider: ['digitalocean'], region: ['sfo2'], }, - }; - const { - body: { hostDetails }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.details, - hostName: 'zeek-sensor-san-francisco', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(hostDetails).to.eql(expectedHostDetails); + }); }); it('Make sure that we get First Seen for a Host without docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*'], docValueFields: [], hostName: 'zeek-sensor-san-francisco', order: 'asc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.firstSeen).to.eql('2019-02-19T19:36:23.561Z'); }); it('Make sure that we get Last Seen for a Host without docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*'], docValueFields: [], hostName: 'zeek-sensor-san-francisco', order: 'desc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.lastSeen).to.eql('2019-02-19T20:42:33.561Z'); }); it('Make sure that we get First Seen for a Host with docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [{ field: '@timestamp', format: 'epoch_millis' }], hostName: 'zeek-sensor-san-francisco', order: 'asc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.firstSeen).to.eql(new Date('2019-02-19T19:36:23.561Z').valueOf()); }); it('Make sure that we get Last Seen for a Host with docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [{ field: '@timestamp', format: 'epoch_millis' }], hostName: 'zeek-sensor-san-francisco', order: 'desc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.lastSeen).to.eql(new Date('2019-02-19T20:42:33.561Z').valueOf()); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts index 632f738d85f362..f38cf406a9dbe5 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts @@ -6,18 +6,27 @@ */ import expect from '@kbn/expect'; -import { HostsKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostsKpiAuthenticationsStrategyResponse, + HostsKpiHostsStrategyResponse, + HostsKpiQueries, + HostsKpiUniqueIpsStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Kpi Hosts', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/kpi_hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/kpi_hosts')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/kpi_hosts') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/kpi_hosts') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -50,88 +59,80 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiHosts data', async () => { - await retry.try(async () => { - const { body: kpiHosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiHosts, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); - expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + const kpiHosts = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts).to.eql(expectedResult.hosts); }); it('Make sure that we get KpiAuthentications data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiAuthentications, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - /* We need a very long timeout to avoid returning just partial data. - ** https://github.com/elastic/kibana/blob/master/x-pack/test/api_integration/apis/search/search.ts#L18 - */ - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); - expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); - expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure).to.eql(expectedResult.authFailure); + expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram); }); it('Make sure that we get KpiUniqueIps data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiUniqueIps, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); - expect(body.uniqueDestinationIpsHistogram!).to.eql( - expectedResult.uniqueDestinationIpsHistogram - ); - expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); - expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.uniqueDestinationIps).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/kpi_hosts') + ); + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/kpi_hosts') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -188,79 +189,69 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiHosts data', async () => { - await retry.try(async () => { - const { body: kpiHosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiHosts, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); - expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + const kpiHosts = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts).to.eql(expectedResult.hosts); }); it('Make sure that we get KpiAuthentications data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiAuthentications, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); - expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); - expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure).to.eql(expectedResult.authFailure); + expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram); }); it('Make sure that we get KpiUniqueIps data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiUniqueIps, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); - expect(body.uniqueDestinationIpsHistogram!).to.eql( - expectedResult.uniqueDestinationIpsHistogram - ); - expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); - expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram!).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts index 53b099bbe18d39..637b69ff1daaae 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts @@ -7,16 +7,28 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { NetworkKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkKpiDnsStrategyResponse, + NetworkKpiNetworkEventsStrategyResponse, + NetworkKpiQueries, + NetworkKpiTlsHandshakesStrategyResponse, + NetworkKpiUniqueFlowsStrategyResponse, + NetworkKpiUniquePrivateIpsStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Kpi Network', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -66,10 +78,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiNetwork uniqueFlows data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniqueFlows, timerange: { interval: '12h', @@ -79,18 +90,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -100,18 +109,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork DNS data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.dns, timerange: { interval: '12h', @@ -121,18 +128,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -142,18 +147,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.tlsHandshakes, timerange: { interval: '12h', @@ -163,18 +166,17 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); }); it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniquePrivateIps, timerange: { interval: '12h', @@ -184,9 +186,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( expectedResult.uniqueDestinationPrivateIps @@ -202,8 +204,12 @@ export default function ({ getService }: FtrProviderContext) { }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -219,10 +225,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiNetwork uniqueFlows data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniqueFlows, timerange: { interval: '12h', @@ -232,18 +237,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); }); it('Make sure that we get KpiNetwork DNS data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.dns, timerange: { interval: '12h', @@ -253,18 +256,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -274,18 +275,17 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.tlsHandshakes, timerange: { interval: '12h', @@ -295,18 +295,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); }); it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniquePrivateIps, timerange: { interval: '12h', @@ -316,9 +314,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( expectedResult.uniqueDestinationPrivateIps diff --git a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts b/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts index 6040ecd1001d94..c7b6bbb84436fd 100644 --- a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts +++ b/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts @@ -6,54 +6,43 @@ */ import expect from '@kbn/expect'; -import request from 'superagent'; import { MatrixHistogramQuery, MatrixHistogramType, + NetworkDnsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; -/** - * Function copied from here: - * test/api_integration/apis/search/bsearch.ts - * - * Splits the JSON lines from bsearch - */ -export const parseBfetchResponse = (resp: request.Response): Array> => { - return resp.text - .trim() - .split('\n') - .map((item) => JSON.parse(item)); -}; - export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - const retry = getService('retry'); + const bsearch = getService('bsearch'); describe('Matrix DNS Histogram', () => { describe('Large data set', () => { - before(() => - esArchiver.load( - 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' - ) + before( + async () => + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' + ) ); - after(() => - esArchiver.unload( - 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' - ) + + after( + async () => + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' + ) ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; it('Make sure that we get dns data without getting bucket errors when querying large volume of data', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { defaultIndex: ['large_volume_dns_data'], docValueFields: [], factoryQueryType: MatrixHistogramQuery, @@ -62,56 +51,14 @@ export default function ({ getService }: FtrProviderContext) { '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', isPtrIncluded: false, timerange: { - interval: '12h', + interval: '12', to: TO, from: FROM, }, - }) - .expect(200); - - if (networkDns.isRunning === true) { - await retry.waitForWithTimeout('bsearch to give us results', 5000, async () => { - const resp = await supertest - .post('/internal/bsearch') - .set('kbn-xsrf', 'true') - .send({ - batch: [ - { - request: { - id: networkDns.id, - defaultIndex: ['large_volume_dns_data'], - docValueFields: [], - factoryQueryType: MatrixHistogramQuery, - histogramType: MatrixHistogramType.dns, - filterQuery: - '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', - isPtrIncluded: false, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - }, - options: { - strategy: 'securitySolutionSearchStrategy', - }, - }, - ], - }); - const parsedResponse = parseBfetchResponse(resp); - // NOTE: I would like this test to be ".to.equal(6604)" but that is flakey as sometimes the query - // does not give me that exact value. It gives me failures as seen here with notes: https://github.com/elastic/kibana/issues/97365 - // I don't think this is a bug with the query but possibly a consistency view issue with interacting with the archive - // so we instead loosen this test up a bit to avoid flake. - expect(parsedResponse[0].result.rawResponse.aggregations.dns_count.value).to.be.above( - 0 - ); - return true; - }); - } else { - expect(networkDns.isRunning).to.equal(false); - expect(networkDns.rawResponse.aggregations.dns_count.value).to.equal(6604); - } + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(networkDns.rawResponse.aggregations?.dns_count).to.eql({ value: 6604 }); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_details.ts b/x-pack/test/api_integration/apis/security_solution/network_details.ts index 0397e7550c9352..07e526c7c4bf5d 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_details.ts @@ -6,59 +6,70 @@ */ import expect from '@kbn/expect'; -import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkDetailsStrategyResponse, + NetworkQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + describe('Network details', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); it('Make sure that we get Network details data', async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const body = await bsearch.send({ + supertest, + options: { ip: '151.205.0.17', defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.details, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); - expect(body.networkDetails!.source!.geo!.continent_name).to.be('North America'); - expect(body.networkDetails!.source!.geo!.location!.lat!).to.be(37.751); - expect(body.networkDetails!.host.os!.platform!).to.eql(['raspbian']); + expect(body.networkDetails.source?.geo.continent_name).to.be('North America'); + expect(body.networkDetails.source?.geo.location?.lat!).to.be(37.751); + expect(body.networkDetails.host?.os?.platform).to.eql(['raspbian']); }); }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default') + ); it('Make sure that we get Network details data', async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const body = await bsearch.send({ + supertest, + options: { ip: '185.53.91.88', defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.details, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); - expect(body.networkDetails!.host.id!).to.eql(['2ce8b1e7d69e4a1d9c6bcddc473da9d9']); - expect(body.networkDetails!.host.name!).to.eql(['zeek-sensor-amsterdam']); - expect(body.networkDetails!.host.os!.platform!).to.eql(['ubuntu']); + expect(body.networkDetails.host?.id).to.eql(['2ce8b1e7d69e4a1d9c6bcddc473da9d9']); + expect(body.networkDetails.host?.name).to.eql(['zeek-sensor-amsterdam']); + expect(body.networkDetails.host?.os?.platform!).to.eql(['ubuntu']); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_dns.ts b/x-pack/test/api_integration/apis/security_solution/network_dns.ts index 80660860a164bc..474c6261babf02 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_dns.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_dns.ts @@ -11,6 +11,7 @@ import { NetworkDnsEdges, Direction, NetworkDnsFields, + NetworkDnsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -18,20 +19,24 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Network DNS', () => { describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/dns')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/dns')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/dns') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/dns') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; it('Make sure that we get Dns data and sorting by uniqueDomains ascending', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { defaultIndex: ['packetbeat-*'], docValueFields: [], factoryQueryType: NetworkQueries.dns, @@ -45,9 +50,9 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkDns.edges.length).to.be(10); expect(networkDns.totalCount).to.be(44); @@ -58,10 +63,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Dns data and sorting by uniqueDomains descending', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { ip: '151.205.0.17', defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.dns, @@ -80,9 +84,9 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkDns.edges.length).to.be(10); expect(networkDns.totalCount).to.be(44); diff --git a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts index af8e5439074921..41b083ab90578c 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts @@ -12,6 +12,7 @@ import { Direction, FlowTargetSourceDest, NetworkTopTablesFields, + NetworkTopNFlowStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -21,20 +22,24 @@ const EDGE_LENGTH = 10; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Network Top N Flow', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2019-02-09T01:57:24.870Z'; const TO = '2019-02-12T01:57:24.870Z'; it('Make sure that we get Source NetworkTopNFlow data with bytes_in descending sort', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.topNFlow, flowTarget: FlowTargetSourceDest.source, @@ -52,9 +57,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); @@ -70,10 +75,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Source NetworkTopNFlow data with bytes_in ascending sort ', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -93,9 +97,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); @@ -111,10 +115,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Destination NetworkTopNFlow data', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -134,9 +137,10 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(154); expect(networkTopNFlow.edges[0].node.destination!.flows).to.be(19); @@ -146,10 +150,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that pagination is working in NetworkTopNFlow query', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -169,9 +172,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_host.ts b/x-pack/test/api_integration/apis/security_solution/overview_host.ts index 09bd09782d2f26..b2a32ac46c3f31 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_host.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_host.ts @@ -8,16 +8,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostsQueries, + HostsOverviewStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Overview Host', () => { describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -41,12 +49,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewHost data', async () => { - const { - body: { overviewHost }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewHost } = await bsearch.send({ + supertest, + options: { defaultIndex: ['auditbeat-*'], factoryQueryType: HostsQueries.overview, timerange: { @@ -56,9 +61,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewHost).to.eql(expectedResult); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_network.ts b/x-pack/test/api_integration/apis/security_solution/overview_network.ts index 00adc903d5733d..ffee9b851ffc0d 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_network.ts @@ -7,16 +7,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkOverviewStrategyResponse, + NetworkQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Overview Network', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -34,12 +42,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -49,16 +54,21 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/overview') + ); + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -75,12 +85,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -90,17 +97,20 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -117,12 +127,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['auditbeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -132,9 +139,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts index 3aefd9f8b597ab..9f1b5fccd9d1af 100644 --- a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts @@ -7,7 +7,11 @@ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; -import { TimelineEventsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + TimelineEventsQueries, + TimelineEventsDetailsStrategyResponse, + TimelineKpiStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -668,54 +672,49 @@ const EXPECTED_KPI_COUNTS = { }; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Timeline Details', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); it('Make sure that we get Event Details data', async () => { - await retry.try(async () => { - const { - body: { data: detailsData }, - } = await supertest - .post('/internal/search/timelineSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: TimelineEventsQueries.details, - docValueFields: [], - indexName: INDEX_NAME, - inspect: false, - eventId: ID, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(sortBy(detailsData, 'field')).to.eql(sortBy(EXPECTED_DATA, 'field')); + const { data: detailsData } = await bsearch.send({ + supertest, + options: { + factoryQueryType: TimelineEventsQueries.details, + docValueFields: [], + indexName: INDEX_NAME, + inspect: false, + eventId: ID, + }, + strategy: 'timelineSearchStrategy', }); + expect(sortBy(detailsData, 'field')).to.eql(sortBy(EXPECTED_DATA, 'field')); }); it('Make sure that we get kpi data', async () => { - await retry.try(async () => { - const { - body: { destinationIpCount, hostCount, processCount, sourceIpCount, userCount }, - } = await supertest - .post('/internal/search/timelineSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { destinationIpCount, hostCount, processCount, sourceIpCount, userCount } = + await bsearch.send({ + supertest, + options: { factoryQueryType: TimelineEventsQueries.kpi, docValueFields: [], indexName: INDEX_NAME, inspect: false, eventId: ID, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect({ destinationIpCount, hostCount, processCount, sourceIpCount, userCount }).to.eql( - EXPECTED_KPI_COUNTS - ); - }); + }, + strategy: 'timelineSearchStrategy', + }); + expect({ destinationIpCount, hostCount, processCount, sourceIpCount, userCount }).to.eql( + EXPECTED_KPI_COUNTS + ); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/tls.ts b/x-pack/test/api_integration/apis/security_solution/tls.ts index 9fa251ded4e6b1..c872844cb3d463 100644 --- a/x-pack/test/api_integration/apis/security_solution/tls.ts +++ b/x-pack/test/api_integration/apis/security_solution/tls.ts @@ -11,6 +11,7 @@ import { Direction, NetworkTlsFields, FlowTarget, + NetworkTlsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -83,17 +84,21 @@ const expectedOverviewSourceResult = { export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Tls Test with Packetbeat', () => { describe('Tls Test', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls') + ); it('Ensure data is returned for FlowTarget.Source', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -112,19 +117,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(tls.edges.length).to.be(1); expect(tls.totalCount).to.be(1); expect(tls.edges[0].node).to.eql(expectedResult); }); it('Ensure data is returned for FlowTarget.Destination', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -143,9 +147,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(tls.edges.length).to.be(1); expect(tls.totalCount).to.be(1); expect(tls.edges[0].node).to.eql(expectedResult); @@ -153,14 +157,17 @@ export default function ({ getService }: FtrProviderContext) { }); describe('Tls Overview Test', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls') + ); it('Ensure data is returned for FlowTarget.Source', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -179,18 +186,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(tls.pageInfo).to.eql(expectedOverviewSourceResult.pageInfo); expect(tls.edges[0]).to.eql(expectedOverviewSourceResult.edges[0]); }); it('Ensure data is returned for FlowTarget.Destination', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -209,9 +216,10 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(tls.pageInfo).to.eql(expectedOverviewDestinationResult.pageInfo); expect(tls.edges[0]).to.eql(expectedOverviewDestinationResult.edges[0]); }); diff --git a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts index d39cc0afb6461a..a6749b27030e1b 100644 --- a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts +++ b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts @@ -13,10 +13,6 @@ import { } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; -interface UncommonProcessesResponse { - body: HostsUncommonProcessesStrategyResponse; -} - const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -24,24 +20,51 @@ const TO = '3000-01-01T00:00:00.000Z'; const TOTAL_COUNT = 3; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('uncommon_processes', () => { - before(() => - esArchiver.load('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') + before( + async () => + await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') ); - after(() => - esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') ); it('should return an edge of length 1 when given a pagination of length 1', async () => { - await retry.try(async () => { - const response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const response = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.uncommonProcesses, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + defaultIndex: ['auditbeat-uncommon-processes'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(response.edges.length).to.be(1); + }); + + describe('when given a pagination of length 2', () => { + it('should return an edge of length 2 ', async () => { + const response = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.uncommonProcesses, sourceId: 'default', timerange: { @@ -53,84 +76,51 @@ export default function ({ getService }: FtrProviderContext) { activePage: 0, cursorStart: 0, fakePossibleCount: 3, - querySize: 1, + querySize: 2, }, defaultIndex: ['auditbeat-uncommon-processes'], docValueFields: [], inspect: false, - }) - .expect(200); - expect(response!.body.edges.length).to.be(1); - }); - }); - - describe('when given a pagination of length 2', () => { - it('should return an edge of length 2 ', async () => { - await retry.try(async () => { - const response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.uncommonProcesses, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 2, - }, - defaultIndex: ['auditbeat-uncommon-processes'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(response!.body.edges.length).to.be(2); + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(response.edges.length).to.be(2); }); }); describe('when given a pagination of length 1', () => { - let response: null | UncommonProcessesResponse = null; + let response: HostsUncommonProcessesStrategyResponse | null = null; before(async () => { - await retry.try(async () => { - response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.uncommonProcesses, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 1, - }, - defaultIndex: ['auditbeat-uncommon-processes'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + response = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.uncommonProcesses, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + defaultIndex: ['auditbeat-uncommon-processes'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); }); it('should return an edge of length 1 ', () => { - expect(response!.body.edges.length).to.be(1); + expect(response?.edges.length).to.be(1); }); it('should return a total count of elements', () => { - expect(response!.body.totalCount).to.be(TOTAL_COUNT); + expect(response?.totalCount).to.be(TOTAL_COUNT); }); it('should return a single data set with pagination of 1', () => { @@ -152,7 +142,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], }; - expect(response!.body.edges[0].node).to.eql(expected); + expect(response?.edges[0].node).to.eql(expected); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/users.ts b/x-pack/test/api_integration/apis/security_solution/users.ts index 84335cc2695ce1..d592c99bf006f1 100644 --- a/x-pack/test/api_integration/apis/security_solution/users.ts +++ b/x-pack/test/api_integration/apis/security_solution/users.ts @@ -11,6 +11,7 @@ import { Direction, NetworkUsersFields, FlowTarget, + NetworkUsersStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -20,53 +21,52 @@ const TO = '3000-01-01T00:00:00.000Z'; const IP = '0.0.0.0'; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + describe('Users', () => { describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/users')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/users')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/users') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/users') + ); it('Ensure data is returned from auditbeat', async () => { - await retry.try(async () => { - const { body: users } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: NetworkQueries.users, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-users'], - docValueFields: [], - ip: IP, - flowTarget: FlowTarget.destination, - sort: { field: NetworkUsersFields.name, direction: Direction.asc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - inspect: false, - /* We need a very long timeout to avoid returning just partial data. - ** https://github.com/elastic/kibana/blob/master/x-pack/test/api_integration/apis/search/search.ts#L18 - */ - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(users.edges.length).to.be(1); - expect(users.totalCount).to.be(1); - expect(users.edges[0].node.user!.id).to.eql(['0']); - expect(users.edges[0].node.user!.name).to.be('root'); - expect(users.edges[0].node.user!.groupId).to.eql(['0']); - expect(users.edges[0].node.user!.groupName).to.eql(['root']); - expect(users.edges[0].node.user!.count).to.be(1); + const users = await bsearch.send({ + supertest, + options: { + factoryQueryType: NetworkQueries.users, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-users'], + docValueFields: [], + ip: IP, + flowTarget: FlowTarget.destination, + sort: { field: NetworkUsersFields.name, direction: Direction.asc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, + }, + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(users.edges.length).to.be(1); + expect(users.totalCount).to.be(1); + expect(users.edges[0].node.user?.id).to.eql(['0']); + expect(users.edges[0].node.user?.name).to.be('root'); + expect(users.edges[0].node.user?.groupId).to.eql(['0']); + expect(users.edges[0].node.user?.groupName).to.eql(['root']); + expect(users.edges[0].node.user?.count).to.be(1); }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts index d5e623989b4606..6c6fcc366782af 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts @@ -70,8 +70,7 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - // Flaky - describe.skip('Rule exception operators for endpoints', () => { + describe('Rule exception operators for endpoints', () => { before(async () => { await esArchiver.load( 'x-pack/test/functional/es_archives/rule_exceptions/endpoint_without_host_type' @@ -113,7 +112,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -135,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -174,7 +173,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -210,7 +209,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -336,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -372,7 +371,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -501,10 +500,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { type: 'macos' }, }, { - os: { type: 'windows' }, + os: { name: 'Macos' }, }, { os: { type: 'windows' }, @@ -546,10 +545,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { type: 'macos' }, }, { - os: { type: 'windows' }, + os: { name: 'Macos' }, }, { os: { type: 'windows' }, @@ -876,7 +875,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts index 89f7693e723589..efff288fddac28 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts @@ -508,6 +508,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); expect(ips.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts index 9fd733789588fa..ff2f6806540470 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts @@ -426,9 +426,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql(['word four']); }); - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('should filter 4 text if all are set as exceptions', async () => { + it('should filter 4 text if all are set as exceptions', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -448,9 +446,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"is not one of" operator', () => { - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('will return 0 results if it cannot find what it is excluding', async () => { + it('will return 0 results if it cannot find what it is excluding', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -489,9 +485,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"exists" operator', () => { - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('will return 0 results if matching against text', async () => { + it('will return 0 results if matching against text', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -577,7 +571,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql(['four', 'two']); }); - it.skip('will return 0 results if we have a list that includes all text', async () => { + it('will return 0 results if we have a list that includes all text', async () => { await importTextFile( supertest, 'text', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts index 3bcf8692d58f94..6d251246671329 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts @@ -337,6 +337,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); @@ -515,6 +516,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); diff --git a/x-pack/test/examples/search_examples/search_sessions_cache.ts b/x-pack/test/examples/search_examples/search_sessions_cache.ts index 7e52849ed2a7e1..0da2de46a1f628 100644 --- a/x-pack/test/examples/search_examples/search_sessions_cache.ts +++ b/x-pack/test/examples/search_examples/search_sessions_cache.ts @@ -27,7 +27,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { return text; } - describe('Search session client side cache', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116537 + describe.skip('Search session client side cache', () => { const appId = 'searchExamples'; before(async function () { diff --git a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts index f7510c3c303184..95ddd0a7b59448 100644 --- a/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts +++ b/x-pack/test/functional/apps/index_lifecycle_management/home_page.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; const policyName = 'testPolicy1'; +const repoName = 'test'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'indexLifecycleManagement']); @@ -16,12 +17,23 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const retry = getService('retry'); const esClient = getService('es'); - // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/114473 and https://github.com/elastic/kibana/issues/114474 - describe.skip('Home page', function () { + describe('Home page', function () { before(async () => { + await esClient.snapshot.createRepository({ + name: repoName, + body: { + type: 'fs', + settings: { + // use one of the values defined in path.repo in test/functional/config.js + location: '/tmp/', + }, + }, + verify: false, + }); await pageObjects.common.navigateToApp('indexLifecycleManagement'); }); after(async () => { + await esClient.snapshot.deleteRepository({ name: repoName }); await esClient.ilm.deleteLifecycle({ name: policyName }); }); @@ -41,6 +53,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { coldEnabled: true, frozenEnabled: true, deleteEnabled: true, + snapshotRepository: repoName, }); await retry.waitFor('navigation back to home page.', async () => { diff --git a/x-pack/test/functional/apps/maps/sample_data.js b/x-pack/test/functional/apps/maps/sample_data.js index a6e4500d30593b..483379b2f49144 100644 --- a/x-pack/test/functional/apps/maps/sample_data.js +++ b/x-pack/test/functional/apps/maps/sample_data.js @@ -108,7 +108,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) { describe('ecommerce', () => { before(async () => { await PageObjects.maps.loadSavedMap('[eCommerce] Orders by Country'); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('United Kingdom'); await PageObjects.maps.toggleLayerVisibility('France'); await PageObjects.maps.toggleLayerVisibility('United States'); @@ -136,7 +136,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) { describe('flights', () => { before(async () => { await PageObjects.maps.loadSavedMap('[Flights] Origin Time Delayed'); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); await PageObjects.maps.closeLegend(); @@ -160,7 +160,7 @@ export default function ({ getPageObjects, getService, updateBaselines }) { describe('web logs', () => { before(async () => { await PageObjects.maps.loadSavedMap('[Logs] Total Requests and Bytes'); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('Total Requests by Destination'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts b/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts new file mode 100644 index 00000000000000..f65653e2c03c5c --- /dev/null +++ b/x-pack/test/functional/apps/ml/anomaly_detection/forecasts.ts @@ -0,0 +1,116 @@ +/* + * 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 '../../../ftr_provider_context'; +import { Job, Datafeed } from '../../../../../plugins/ml/common/types/anomaly_detection_jobs'; + +// @ts-expect-error not full interface +const JOB_CONFIG: Job = { + job_id: `fq_single_1_smv`, + description: 'count() on farequote dataset with 15m bucket span', + groups: ['farequote', 'automated', 'single-metric'], + analysis_config: { + bucket_span: '15m', + influencers: [], + detectors: [ + { + function: 'count', + }, + ], + }, + data_description: { time_field: '@timestamp' }, + analysis_limits: { model_memory_limit: '10mb' }, + model_plot_config: { enabled: true }, +}; + +// @ts-expect-error not full interface +const DATAFEED_CONFIG: Datafeed = { + datafeed_id: 'datafeed-fq_single_1_smv', + indices: ['ft_farequote'], + job_id: 'fq_single_1_smv', + query: { bool: { must: [{ match_all: {} }] } }, +}; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const ml = getService('ml'); + + describe('forecasts', function () { + this.tags(['mlqa']); + + describe('with single metric job', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); + await ml.testResources.setKibanaTimeZoneToUTC(); + + await ml.api.createAndRunAnomalyDetectionLookbackJob(JOB_CONFIG, DATAFEED_CONFIG); + await ml.securityUI.loginAsMlPowerUser(); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + }); + + it('opens a job from job list link', async () => { + await ml.testExecution.logTestStep('navigate to job list'); + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + + await ml.testExecution.logTestStep('open job in single metric viewer'); + await ml.jobTable.waitForJobsToLoad(); + await ml.jobTable.filterWithSearchString(JOB_CONFIG.job_id, 1); + + await ml.jobTable.clickOpenJobInSingleMetricViewerButton(JOB_CONFIG.job_id); + await ml.commonUI.waitForMlLoadingIndicatorToDisappear(); + }); + + it('displays job results', async () => { + await ml.testExecution.logTestStep('pre-fills the job selection'); + await ml.jobSelection.assertJobSelection([JOB_CONFIG.job_id]); + + await ml.testExecution.logTestStep('pre-fills the detector input'); + await ml.singleMetricViewer.assertDetectorInputExist(); + await ml.singleMetricViewer.assertDetectorInputValue('0'); + + await ml.testExecution.logTestStep('displays the chart'); + await ml.singleMetricViewer.assertChartExist(); + + await ml.testExecution.logTestStep('should not display the forecasts toggle checkbox'); + await ml.forecast.assertForecastCheckboxMissing(); + + await ml.testExecution.logTestStep('should open the forecasts modal'); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastModalRunButtonEnabled(true); + + await ml.testExecution.logTestStep('should run the forecast and close the modal'); + await ml.forecast.clickForecastModalRunButton(); + + await ml.testExecution.logTestStep('should display the forecasts toggle checkbox'); + await ml.forecast.assertForecastCheckboxExists(); + + await ml.testExecution.logTestStep( + 'should display the forecast in the single metric chart' + ); + await ml.forecast.assertForecastChartElementsExists(); + + await ml.testExecution.logTestStep('should hide the forecast in the single metric chart'); + await ml.forecast.clickForecastCheckbox(); + await ml.forecast.assertForecastChartElementsHidden(); + + await ml.testExecution.logTestStep('should open the forecasts modal and list the forecast'); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastTableExists(); + await ml.forecast.assertForecastTableNotEmpty(); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/index.ts b/x-pack/test/functional/apps/ml/anomaly_detection/index.ts index d87da8469db118..ed5f618f86644c 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/index.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/index.ts @@ -24,5 +24,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./annotations')); loadTestFile(require.resolve('./aggregated_scripted_job')); loadTestFile(require.resolve('./custom_urls')); + loadTestFile(require.resolve('./forecasts')); }); } diff --git a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts index 448774f1a0c7f1..356e3822179647 100644 --- a/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/full_ml_access.ts @@ -237,11 +237,11 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'should display the forecast modal with enabled run button' ); - await ml.singleMetricViewer.assertForecastButtonExists(); - await ml.singleMetricViewer.assertForecastButtonEnabled(true); - await ml.singleMetricViewer.openForecastModal(); - await ml.singleMetricViewer.assertForecastModalRunButtonEnabled(true); - await ml.singleMetricViewer.closeForecastModal(); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastModalRunButtonEnabled(true); + await ml.forecast.closeForecastModal(); }); it('should display elements on Anomaly Explorer page correctly', async () => { diff --git a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts index b96da748507866..be57904b944514 100644 --- a/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts +++ b/x-pack/test/functional/apps/ml/permissions/read_ml_access.ts @@ -230,11 +230,11 @@ export default function ({ getService }: FtrProviderContext) { await ml.testExecution.logTestStep( 'should display the forecast modal with disabled run button' ); - await ml.singleMetricViewer.assertForecastButtonExists(); - await ml.singleMetricViewer.assertForecastButtonEnabled(true); - await ml.singleMetricViewer.openForecastModal(); - await ml.singleMetricViewer.assertForecastModalRunButtonEnabled(false); - await ml.singleMetricViewer.closeForecastModal(); + await ml.forecast.assertForecastButtonExists(); + await ml.forecast.assertForecastButtonEnabled(true); + await ml.forecast.openForecastModal(); + await ml.forecast.assertForecastModalRunButtonEnabled(false); + await ml.forecast.closeForecastModal(); }); it('should display elements on Anomaly Explorer page correctly', async () => { diff --git a/x-pack/test/functional/services/ml/forecast.ts b/x-pack/test/functional/services/ml/forecast.ts new file mode 100644 index 00000000000000..c26216c97adfe3 --- /dev/null +++ b/x-pack/test/functional/services/ml/forecast.ts @@ -0,0 +1,126 @@ +/* + * 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 '../../ftr_provider_context'; + +export function MachineLearningForecastProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertForecastButtonExists() { + await testSubjects.existOrFail( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' + ); + }, + + async assertForecastButtonEnabled(expectedValue: boolean) { + const isEnabled = await testSubjects.isEnabled( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' + ); + expect(isEnabled).to.eql( + expectedValue, + `Expected "forecast" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ + isEnabled ? 'enabled' : 'disabled' + }')` + ); + }, + + async assertForecastChartElementsExists() { + await testSubjects.existOrFail(`mlForecastArea`, { + timeout: 30 * 1000, + }); + await testSubjects.existOrFail(`mlForecastValuesline`, { + timeout: 30 * 1000, + }); + await testSubjects.existOrFail(`mlForecastMarkers`, { + timeout: 30 * 1000, + }); + }, + + async assertForecastChartElementsHidden() { + await testSubjects.missingOrFail(`mlForecastArea`, { + allowHidden: true, + timeout: 30 * 1000, + }); + await testSubjects.missingOrFail(`mlForecastValuesline`, { + allowHidden: true, + timeout: 30 * 1000, + }); + await testSubjects.missingOrFail(`mlForecastMarkers`, { + allowHidden: true, + timeout: 30 * 1000, + }); + }, + + async assertForecastCheckboxExists() { + await testSubjects.existOrFail(`mlForecastCheckbox`, { + timeout: 30 * 1000, + }); + }, + + async assertForecastCheckboxMissing() { + await testSubjects.missingOrFail(`mlForecastCheckbox`, { + timeout: 30 * 1000, + }); + }, + + async clickForecastCheckbox() { + await testSubjects.click('mlForecastCheckbox'); + }, + + async openForecastModal() { + await testSubjects.click( + 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' + ); + await testSubjects.existOrFail('mlModalForecast'); + }, + + async closeForecastModal() { + await testSubjects.click('mlModalForecast > mlModalForecastButtonClose'); + await this.assertForecastModalMissing(); + }, + + async assertForecastModalMissing() { + await testSubjects.missingOrFail(`mlModalForecast`, { + timeout: 30 * 1000, + }); + }, + + async assertForecastModalRunButtonEnabled(expectedValue: boolean) { + const isEnabled = await testSubjects.isEnabled('mlModalForecast > mlModalForecastButtonRun'); + expect(isEnabled).to.eql( + expectedValue, + `Expected forecast "run" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ + isEnabled ? 'enabled' : 'disabled' + }')` + ); + }, + + async assertForecastTableExists() { + await testSubjects.existOrFail('mlModalForecast > mlModalForecastTable'); + }, + + async clickForecastModalRunButton() { + await testSubjects.click('mlModalForecast > mlModalForecastButtonRun'); + await this.assertForecastModalMissing(); + }, + + async getForecastTableRows() { + return await testSubjects.findAll('mlModalForecastTable > ~mlForecastsListRow'); + }, + + async assertForecastTableNotEmpty() { + const tableRows = await this.getForecastTableRows(); + expect(tableRows.length).to.be.greaterThan( + 0, + `Forecast table should have at least one row (got '${tableRows.length}')` + ); + }, + }; +} diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index 17302b27822237..4b48e4c0269eb9 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -24,6 +24,7 @@ import { MachineLearningDataVisualizerProvider } from './data_visualizer'; import { MachineLearningDataVisualizerFileBasedProvider } from './data_visualizer_file_based'; import { MachineLearningDataVisualizerIndexBasedProvider } from './data_visualizer_index_based'; import { MachineLearningDataVisualizerIndexPatternManagementProvider } from './data_visualizer_index_pattern_management'; +import { MachineLearningForecastProvider } from './forecast'; import { MachineLearningJobManagementProvider } from './job_management'; import { MachineLearningJobSelectionProvider } from './job_selection'; import { MachineLearningJobSourceSelectionProvider } from './job_source_selection'; @@ -92,6 +93,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dataVisualizerIndexPatternManagement = MachineLearningDataVisualizerIndexPatternManagementProvider(context, dataVisualizerTable); + const forecast = MachineLearningForecastProvider(context); const jobAnnotations = MachineLearningJobAnnotationsProvider(context); const jobManagement = MachineLearningJobManagementProvider(context, api); const jobSelection = MachineLearningJobSelectionProvider(context); @@ -145,6 +147,7 @@ export function MachineLearningProvider(context: FtrProviderContext) { dataVisualizerIndexBased, dataVisualizerIndexPatternManagement, dataVisualizerTable, + forecast, jobAnnotations, jobManagement, jobSelection, diff --git a/x-pack/test/functional/services/ml/security_common.ts b/x-pack/test/functional/services/ml/security_common.ts index 54d2fa48a826f0..925565143bda0b 100644 --- a/x-pack/test/functional/services/ml/security_common.ts +++ b/x-pack/test/functional/services/ml/security_common.ts @@ -58,7 +58,7 @@ export function MachineLearningSecurityCommonProvider({ getService }: FtrProvide { name: 'ft_ml_ui_extras', elasticsearch: { - cluster: ['manage_ingest_pipelines', 'monitor'], + cluster: ['manage_ingest_pipelines'], }, kibana: [], }, diff --git a/x-pack/test/functional/services/ml/single_metric_viewer.ts b/x-pack/test/functional/services/ml/single_metric_viewer.ts index ac3fd67e3f94e8..29f1ded74debaa 100644 --- a/x-pack/test/functional/services/ml/single_metric_viewer.ts +++ b/x-pack/test/functional/services/ml/single_metric_viewer.ts @@ -22,24 +22,6 @@ export function MachineLearningSingleMetricViewerProvider( await testSubjects.existOrFail('mlNoSingleMetricJobsFound'); }, - async assertForecastButtonExists() { - await testSubjects.existOrFail( - 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' - ); - }, - - async assertForecastButtonEnabled(expectedValue: boolean) { - const isEnabled = await testSubjects.isEnabled( - 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' - ); - expect(isEnabled).to.eql( - expectedValue, - `Expected "forecast" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ - isEnabled ? 'enabled' : 'disabled' - }')` - ); - }, - async assertDetectorInputExist() { await testSubjects.existOrFail( 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerDetectorSelect' @@ -97,28 +79,6 @@ export function MachineLearningSingleMetricViewerProvider( }); }, - async openForecastModal() { - await testSubjects.click( - 'mlSingleMetricViewerSeriesControls > mlSingleMetricViewerButtonForecast' - ); - await testSubjects.existOrFail('mlModalForecast'); - }, - - async closeForecastModal() { - await testSubjects.click('mlModalForecast > mlModalForecastButtonClose'); - await testSubjects.missingOrFail('mlModalForecast'); - }, - - async assertForecastModalRunButtonEnabled(expectedValue: boolean) { - const isEnabled = await testSubjects.isEnabled('mlModalForecast > mlModalForecastButtonRun'); - expect(isEnabled).to.eql( - expectedValue, - `Expected forecast "run" button to be '${expectedValue ? 'enabled' : 'disabled'}' (got '${ - isEnabled ? 'enabled' : 'disabled' - }')` - ); - }, - async openAnomalyExplorer() { await testSubjects.click('mlAnomalyResultsViewSelectorExplorer'); await testSubjects.existOrFail('mlPageAnomalyExplorer'); diff --git a/x-pack/test/load/runner.ts b/x-pack/test/load/runner.ts index 0bea5992f55394..c48a8e33d6eef6 100644 --- a/x-pack/test/load/runner.ts +++ b/x-pack/test/load/runner.ts @@ -28,7 +28,11 @@ if (!Fs.existsSync(gatlingProjectRootPath)) { ); } -const dropEmptyLines = (s: string) => s.split(',').filter((i) => i.length > 0); +const dropEmptyLines = (s: string) => + s + .split(',') + .filter((i) => i.length > 0) + .map((i) => (i.includes('.') ? i : `branch.${i}`)); const simulationClasses = dropEmptyLines(simulationEntry); const simulationsRootPath = resolve(gatlingProjectRootPath, baseSimulationPath); diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts index 159115e2054e1e..f6654ff5a6b1d4 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts @@ -30,6 +30,5 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./spaces')); loadTestFile(require.resolve('./usage')); loadTestFile(require.resolve('./ilm_migration_apis')); - loadTestFile(require.resolve('./search_frozen_indices')); }); } diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts b/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts deleted file mode 100644 index daa749649e250e..00000000000000 --- a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts +++ /dev/null @@ -1,127 +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 { FtrProviderContext } from '../ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function ({ getService }: FtrProviderContext) { - const kibanaServer = getService('kibanaServer'); - const supertestSvc = getService('supertest'); - const esSupertest = getService('esSupertest'); - const indexPatternId = 'cool-test-index-pattern'; - - async function callExportAPI() { - const job = { - browserTimezone: 'UTC', - columns: ['@timestamp', 'ip', 'utilization'], - searchSource: { - fields: [{ field: '*', include_unmapped: 'true' }], - filter: [ - { - meta: { field: '@timestamp', index: indexPatternId, params: {} }, - range: { - '@timestamp': { - format: 'strict_date_optional_time', - gte: '2020-08-24T00:00:00.000Z', - lte: '2022-08-24T21:40:48.346Z', - }, - }, - }, - ], - index: indexPatternId, - parent: { filter: [], index: indexPatternId, query: { language: 'kuery', query: '' } }, - sort: [{ '@timestamp': 'desc' }], - trackTotalHits: true, - }, - title: 'Test search', - }; - - return await supertestSvc - .post(`/api/reporting/v1/generate/immediate/csv_searchsource`) - .set('kbn-xsrf', 'xxx') - .send(job); - } - - describe('Frozen indices search', () => { - const reset = async () => { - await kibanaServer.uiSettings.replace({ 'search:includeFrozen': false }); - try { - await esSupertest.delete('/test1,test2,test3'); - await kibanaServer.savedObjects.delete({ type: 'index-pattern', id: indexPatternId }); - } catch (err) { - // ignore 404 error - } - }; - - before(reset); - after(reset); - - it('Search includes frozen indices based on Advanced Setting', async () => { - await kibanaServer.uiSettings.update({ 'csv:quoteValues': true }); - - // setup: add multiple indices of test data - await Promise.all([ - esSupertest - .post('/test1/_doc') - .send({ '@timestamp': '2021-08-24T21:36:40Z', ip: '43.98.8.183', utilization: 18725 }), - esSupertest - .post('/test2/_doc') - .send({ '@timestamp': '2021-08-21T09:36:40Z', ip: '63.91.103.79', utilization: 8480 }), - esSupertest - .post('/test3/_doc') - .send({ '@timestamp': '2021-08-17T21:36:40Z', ip: '139.108.162.171', utilization: 3078 }), - ]); - await esSupertest.post('/test*/_refresh'); - - // setup: create index pattern - const indexPatternCreateResponse = await kibanaServer.savedObjects.create({ - type: 'index-pattern', - id: indexPatternId, - overwrite: true, - attributes: { title: 'test*', timeFieldName: '@timestamp' }, - }); - expect(indexPatternCreateResponse.id).to.be(indexPatternId); - - // 1. check the initial data with a CSV export - const initialSearch = await callExportAPI(); - expectSnapshot(initialSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - - // 2. freeze an index in the pattern - await esSupertest.post('/test3/_freeze').expect(200); - await esSupertest.post('/test*/_refresh').expect(200); - - // 3. recheck the search results - const afterFreezeSearch = await callExportAPI(); - expectSnapshot(afterFreezeSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - " - `); - - // 4. update setting to allow searching frozen data - await kibanaServer.uiSettings.update({ 'search:includeFrozen': true }); - - // 5. recheck the search results - const afterAllowSearch = await callExportAPI(); - expectSnapshot(afterAllowSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - }); - }); -} diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 70d60ba5c1b67f..d1cfddbca3a9c7 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -15,7 +15,8 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe('endpoint', function () { + // FAILING: https://github.com/elastic/kibana/issues/72874 + describe.skip('endpoint', function () { const ingestManager = getService('ingestManager'); const log = getService('log'); const endpointTestResources = getService('endpointTestResources'); diff --git a/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts b/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts index e44f29c41640fe..1e9cfe3eb841b3 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts @@ -135,6 +135,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -164,6 +166,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -183,6 +187,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to access "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) diff --git a/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts b/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts index 0a73009196bafd..91ad87737805f7 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts @@ -117,6 +117,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -145,6 +147,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to access "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) diff --git a/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts b/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts index da296e5a4f60aa..fb8d8c6c59a9db 100644 --- a/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts +++ b/x-pack/test/upgrade/apps/maps/maps_smoke_tests.ts @@ -111,7 +111,7 @@ export default function ({ ); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.maps.waitForLayersToLoad(); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('United Kingdom'); await PageObjects.maps.toggleLayerVisibility('France'); await PageObjects.maps.toggleLayerVisibility('United States'); @@ -141,7 +141,7 @@ export default function ({ ); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.maps.waitForLayersToLoad(); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); await PageObjects.maps.closeLegend(); @@ -167,7 +167,7 @@ export default function ({ ); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.maps.waitForLayersToLoad(); - await PageObjects.maps.toggleLayerVisibility('Road map'); + await PageObjects.maps.toggleLayerVisibility('Road map - desaturated'); await PageObjects.maps.toggleLayerVisibility('Total Requests by Destination'); await PageObjects.timePicker.setCommonlyUsedTime('sample_data range'); await PageObjects.maps.enterFullScreen(); diff --git a/x-pack/test/usage_collection/test_suites/application_usage/index.ts b/x-pack/test/usage_collection/test_suites/application_usage/index.ts index fc53c8ddf5ed37..4ba45b4bf9e126 100644 --- a/x-pack/test/usage_collection/test_suites/application_usage/index.ts +++ b/x-pack/test/usage_collection/test_suites/application_usage/index.ts @@ -10,7 +10,8 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { applicationUsageSchema } from '../../../../../src/plugins/kibana_usage_collection/server/collectors/application_usage/schema'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - describe('Application Usage', function () { + // FLAKY: https://github.com/elastic/kibana/issues/90536 + describe.skip('Application Usage', function () { this.tags('ciGroup1'); const { common } = getPageObjects(['common']); const browser = getService('browser'); diff --git a/yarn.lock b/yarn.lock index 3e4e3ad4ed2243..56d5cf17791eaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2337,10 +2337,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@38.0.1": - version "38.0.1" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.0.1.tgz#9c1db7e0f1de869e0b2b505e192bbb9d62d60dc8" - integrity sha512-i9mIA3Ji9jSjuFDtuh9gV1xpCl3sbBEDgJiOgLVt04pr/qZH2W+tr3AV5yHvjsR7Te0Pmh/Cm5wLBvFKaI1nIA== +"@elastic/charts@38.1.0": + version "38.1.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.1.0.tgz#52146564c0e399da2267c10ec4536c33e50969e4" + integrity sha512-u2hQ8+daCvqapKQiFqN8QHWTz3OXby5Xf/ta1Dv59KTDTFIinCZD/M8PyD3MapbKx4b67hL7UxbErqr4rZ8jeA== dependencies: "@popperjs/core" "^2.4.0" chroma-js "^2.1.0" @@ -2351,6 +2351,7 @@ d3-interpolate "^1.4.0" d3-scale "^1.0.7" d3-shape "^1.3.4" + luxon "^1.25.0" prop-types "^15.7.2" re-reselect "^3.4.0" react-redux "^7.1.0" @@ -19720,6 +19721,11 @@ lru-queue@0.1: dependencies: es5-ext "~0.10.2" +luxon@^1.25.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"