diff --git a/.buildkite/scripts/build_kibana.sh b/.buildkite/scripts/build_kibana.sh index 7a9878b5bcd13..e26d7790215f3 100755 --- a/.buildkite/scripts/build_kibana.sh +++ b/.buildkite/scripts/build_kibana.sh @@ -5,7 +5,11 @@ set -euo pipefail export KBN_NP_PLUGINS_BUILT=true echo "--- Build Kibana Distribution" -node scripts/build --debug +if [[ "${GITHUB_PR_LABELS:-}" == *"ci:build-all-platforms"* ]]; then + node scripts/build --all-platforms --skip-os-packages +else + node scripts/build +fi echo "--- Archive Kibana Distribution" linuxBuild="$(find "$KIBANA_DIR/target" -name 'kibana-*-linux-x86_64.tar.gz')" diff --git a/.buildkite/scripts/post_build_kibana.sh b/.buildkite/scripts/post_build_kibana.sh index 2194414dd22d3..5f26c80ddb6b6 100755 --- a/.buildkite/scripts/post_build_kibana.sh +++ b/.buildkite/scripts/post_build_kibana.sh @@ -12,7 +12,6 @@ fi echo "--- Upload Build Artifacts" # Moving to `target/` first will keep `buildkite-agent` from including directories in the artifact name cd "$KIBANA_DIR/target" -mv kibana-*-linux-x86_64.tar.gz kibana-default.tar.gz -buildkite-agent artifact upload kibana-default.tar.gz -buildkite-agent artifact upload kibana-default-plugins.tar.gz +cp kibana-*-linux-x86_64.tar.gz kibana-default.tar.gz +buildkite-agent artifact upload "./*.tar.gz;./*.zip" cd - diff --git a/.buildkite/scripts/steps/es_snapshots/build.sh b/.buildkite/scripts/steps/es_snapshots/build.sh index 91b5004594a6d..975d5944da883 100755 --- a/.buildkite/scripts/steps/es_snapshots/build.sh +++ b/.buildkite/scripts/steps/es_snapshots/build.sh @@ -61,7 +61,14 @@ export DOCKER_TLS_CERTDIR="$CERTS_DIR" export DOCKER_HOST=localhost:2377 echo "--- Build Elasticsearch" -./gradlew -Dbuild.docker=true assemble --parallel +./gradlew \ + :distribution:archives:darwin-aarch64-tar:assemble \ + :distribution:archives:darwin-tar:assemble \ + :distribution:docker:docker-export:assemble \ + :distribution:archives:linux-aarch64-tar:assemble \ + :distribution:archives:linux-tar:assemble \ + :distribution:archives:windows-zip:assemble \ + --parallel echo "--- Create distribution archives" find distribution -type f \( -name 'elasticsearch-*-*-*-*.tar.gz' -o -name 'elasticsearch-*-*-*-*.zip' \) -not -path '*no-jdk*' -not -path '*build-context*' -exec cp {} "$destination" \; diff --git a/.eslintrc.js b/.eslintrc.js index 7d96316e41a99..6f62f953c9a4c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -902,7 +902,12 @@ module.exports = { }, /** - * Security Solution overrides + * Security Solution overrides. These rules below are maintained and owned by + * the people within the security-solution-platform team. Please see ping them + * or check with them if you are encountering issues, have suggestions, or would + * like to add, change, or remove any particular rule. Linters, Typescript, and rules + * evolve and change over time just like coding styles, so please do not hesitate to + * reach out. */ { // front end and common typescript and javascript files only @@ -925,6 +930,22 @@ module.exports = { ], }, }, + { + // typescript only for front and back end, but excludes the test files. + // We use this section to add rules in which we do not want to apply to test files. + // This should be a very small set as most linter rules are useful for tests as well. + files: [ + 'x-pack/plugins/security_solution/**/*.{ts,tsx}', + 'x-pack/plugins/timelines/**/*.{ts,tsx}', + ], + excludedFiles: [ + 'x-pack/plugins/security_solution/**/*.{test,mock,test_helper}.{ts,tsx}', + 'x-pack/plugins/timelines/**/*.{test,mock,test_helper}.{ts,tsx}', + ], + rules: { + '@typescript-eslint/no-non-null-assertion': 'error', + }, + }, { // typescript only for front and back end files: [ @@ -1043,7 +1064,12 @@ module.exports = { }, /** - * Lists overrides + * Lists overrides. These rules below are maintained and owned by + * the people within the security-solution-platform team. Please see ping them + * or check with them if you are encountering issues, have suggestions, or would + * like to add, change, or remove any particular rule. Linters, Typescript, and rules + * evolve and change over time just like coding styles, so please do not hesitate to + * reach out. */ { // front end and common typescript and javascript files only @@ -1218,8 +1244,14 @@ module.exports = { ], }, }, + /** - * Metrics entities overrides + * Metrics entities overrides. These rules below are maintained and owned by + * the people within the security-solution-platform team. Please see ping them + * or check with them if you are encountering issues, have suggestions, or would + * like to add, change, or remove any particular rule. Linters, Typescript, and rules + * evolve and change over time just like coding styles, so please do not hesitate to + * reach out. */ { // front end and common typescript and javascript files only 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 51101c8804680..d90972d327041 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,4 +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 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 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;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: 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<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: 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;
};
} | | \ No newline at end of file +| [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;
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<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: 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/development/core/server/kibana-plugin-core-server.deprecationsdetails.correctiveactions.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.correctiveactions.md similarity index 66% rename from docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.correctiveactions.md rename to docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.correctiveactions.md index d7d10651033bf..273945166735b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.correctiveactions.md +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.correctiveactions.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [correctiveActions](./kibana-plugin-core-server.deprecationsdetails.correctiveactions.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) > [correctiveActions](./kibana-plugin-core-server.basedeprecationdetails.correctiveactions.md) -## DeprecationsDetails.correctiveActions property +## BaseDeprecationDetails.correctiveActions property corrective action needed to fix this deprecation. diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.deprecationtype.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.deprecationtype.md similarity index 70% rename from docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.deprecationtype.md rename to docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.deprecationtype.md index 3a76bc60ee630..072dfd17418f9 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.deprecationtype.md +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.deprecationtype.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [deprecationType](./kibana-plugin-core-server.deprecationsdetails.deprecationtype.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) > [deprecationType](./kibana-plugin-core-server.basedeprecationdetails.deprecationtype.md) -## DeprecationsDetails.deprecationType property +## BaseDeprecationDetails.deprecationType property (optional) Used to identify between different deprecation types. Example use case: in Upgrade Assistant, we may want to allow the user to sort by deprecation type or show each type in a separate tab. diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.documentationurl.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.documentationurl.md similarity index 53% rename from docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.documentationurl.md rename to docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.documentationurl.md index 457cf7b61dac8..c8f0762acdce6 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.documentationurl.md +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.documentationurl.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [documentationUrl](./kibana-plugin-core-server.deprecationsdetails.documentationurl.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) > [documentationUrl](./kibana-plugin-core-server.basedeprecationdetails.documentationurl.md) -## DeprecationsDetails.documentationUrl property +## BaseDeprecationDetails.documentationUrl property (optional) link to the documentation for more details on the deprecation. diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.level.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.level.md similarity index 66% rename from docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.level.md rename to docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.level.md index 64ad22e2c87fb..ad755805d00b9 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.level.md +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.level.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [level](./kibana-plugin-core-server.deprecationsdetails.level.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) > [level](./kibana-plugin-core-server.basedeprecationdetails.level.md) -## DeprecationsDetails.level property +## BaseDeprecationDetails.level property levels: - warning: will not break deployment upon upgrade - critical: needs to be addressed before upgrade. - fetch\_error: Deprecations service failed to grab the deprecation details for the domain. diff --git a/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.md new file mode 100644 index 0000000000000..3e47865062352 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) + +## BaseDeprecationDetails interface + +Base properties shared by all types of deprecations + +Signature: + +```typescript +export interface BaseDeprecationDetails +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [correctiveActions](./kibana-plugin-core-server.basedeprecationdetails.correctiveactions.md) | {
api?: {
path: string;
method: 'POST' | 'PUT';
body?: {
[key: string]: any;
};
omitContextFromBody?: boolean;
};
manualSteps: string[];
} | corrective action needed to fix this deprecation. | +| [deprecationType](./kibana-plugin-core-server.basedeprecationdetails.deprecationtype.md) | 'config' | 'feature' | (optional) Used to identify between different deprecation types. Example use case: in Upgrade Assistant, we may want to allow the user to sort by deprecation type or show each type in a separate tab.Feel free to add new types if necessary. Predefined types are necessary to reduce having similar definitions with different keywords across kibana deprecations. | +| [documentationUrl](./kibana-plugin-core-server.basedeprecationdetails.documentationurl.md) | string | (optional) link to the documentation for more details on the deprecation. | +| [level](./kibana-plugin-core-server.basedeprecationdetails.level.md) | 'warning' | 'critical' | 'fetch_error' | levels: - warning: will not break deployment upon upgrade - critical: needs to be addressed before upgrade. - fetch\_error: Deprecations service failed to grab the deprecation details for the domain. | +| [message](./kibana-plugin-core-server.basedeprecationdetails.message.md) | string | The description message to be displayed for the deprecation. Check the README for writing deprecations in src/core/server/deprecations/README.mdx | +| [requireRestart](./kibana-plugin-core-server.basedeprecationdetails.requirerestart.md) | boolean | (optional) specify the fix for this deprecation requires a full kibana restart. | +| [title](./kibana-plugin-core-server.basedeprecationdetails.title.md) | string | The title of the deprecation. Check the README for writing deprecations in src/core/server/deprecations/README.mdx | + diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.message.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.message.md similarity index 60% rename from docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.message.md rename to docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.message.md index 906ce8118f95b..5802bc316cc08 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.message.md +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.message.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [message](./kibana-plugin-core-server.deprecationsdetails.message.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) > [message](./kibana-plugin-core-server.basedeprecationdetails.message.md) -## DeprecationsDetails.message property +## BaseDeprecationDetails.message property The description message to be displayed for the deprecation. Check the README for writing deprecations in `src/core/server/deprecations/README.mdx` diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.requirerestart.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.requirerestart.md similarity index 54% rename from docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.requirerestart.md rename to docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.requirerestart.md index 85bddd9436e73..3f589600d0458 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.requirerestart.md +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.requirerestart.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [requireRestart](./kibana-plugin-core-server.deprecationsdetails.requirerestart.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) > [requireRestart](./kibana-plugin-core-server.basedeprecationdetails.requirerestart.md) -## DeprecationsDetails.requireRestart property +## BaseDeprecationDetails.requireRestart property (optional) specify the fix for this deprecation requires a full kibana restart. diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.title.md b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.title.md similarity index 59% rename from docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.title.md rename to docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.title.md index e8907688f6e5e..b6788a4faa7c5 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.title.md +++ b/docs/development/core/server/kibana-plugin-core-server.basedeprecationdetails.title.md @@ -1,8 +1,8 @@ -[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) > [title](./kibana-plugin-core-server.deprecationsdetails.title.md) +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) > [title](./kibana-plugin-core-server.basedeprecationdetails.title.md) -## DeprecationsDetails.title property +## BaseDeprecationDetails.title property The title of the deprecation. Check the README for writing deprecations in `src/core/server/deprecations/README.mdx` diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.configpath.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.configpath.md new file mode 100644 index 0000000000000..7af6c16d86e4a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.configpath.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationDetails](./kibana-plugin-core-server.configdeprecationdetails.md) > [configPath](./kibana-plugin-core-server.configdeprecationdetails.configpath.md) + +## ConfigDeprecationDetails.configPath property + +Signature: + +```typescript +configPath: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.deprecationtype.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.deprecationtype.md new file mode 100644 index 0000000000000..fb3737062f986 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.deprecationtype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationDetails](./kibana-plugin-core-server.configdeprecationdetails.md) > [deprecationType](./kibana-plugin-core-server.configdeprecationdetails.deprecationtype.md) + +## ConfigDeprecationDetails.deprecationType property + +Signature: + +```typescript +deprecationType: 'config'; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.md b/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.md new file mode 100644 index 0000000000000..d1ccbb0b6164a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.configdeprecationdetails.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ConfigDeprecationDetails](./kibana-plugin-core-server.configdeprecationdetails.md) + +## ConfigDeprecationDetails interface + + +Signature: + +```typescript +export interface ConfigDeprecationDetails extends BaseDeprecationDetails +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [configPath](./kibana-plugin-core-server.configdeprecationdetails.configpath.md) | string | | +| [deprecationType](./kibana-plugin-core-server.configdeprecationdetails.deprecationtype.md) | 'config' | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md index 2ff9f4b792f5d..d8ced1da62416 100644 --- a/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md +++ b/docs/development/core/server/kibana-plugin-core-server.deprecationsdetails.md @@ -2,24 +2,11 @@ [Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) -## DeprecationsDetails interface +## DeprecationsDetails type Signature: ```typescript -export interface DeprecationsDetails +export declare type DeprecationsDetails = ConfigDeprecationDetails | FeatureDeprecationDetails; ``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [correctiveActions](./kibana-plugin-core-server.deprecationsdetails.correctiveactions.md) | {
api?: {
path: string;
method: 'POST' | 'PUT';
body?: {
[key: string]: any;
};
omitContextFromBody?: boolean;
};
manualSteps: string[];
} | corrective action needed to fix this deprecation. | -| [deprecationType](./kibana-plugin-core-server.deprecationsdetails.deprecationtype.md) | 'config' | 'feature' | (optional) Used to identify between different deprecation types. Example use case: in Upgrade Assistant, we may want to allow the user to sort by deprecation type or show each type in a separate tab.Feel free to add new types if necessary. Predefined types are necessary to reduce having similar definitions with different keywords across kibana deprecations. | -| [documentationUrl](./kibana-plugin-core-server.deprecationsdetails.documentationurl.md) | string | (optional) link to the documentation for more details on the deprecation. | -| [level](./kibana-plugin-core-server.deprecationsdetails.level.md) | 'warning' | 'critical' | 'fetch_error' | levels: - warning: will not break deployment upon upgrade - critical: needs to be addressed before upgrade. - fetch\_error: Deprecations service failed to grab the deprecation details for the domain. | -| [message](./kibana-plugin-core-server.deprecationsdetails.message.md) | string | The description message to be displayed for the deprecation. Check the README for writing deprecations in src/core/server/deprecations/README.mdx | -| [requireRestart](./kibana-plugin-core-server.deprecationsdetails.requirerestart.md) | boolean | (optional) specify the fix for this deprecation requires a full kibana restart. | -| [title](./kibana-plugin-core-server.deprecationsdetails.title.md) | string | The title of the deprecation. Check the README for writing deprecations in src/core/server/deprecations/README.mdx | - diff --git a/docs/development/core/server/kibana-plugin-core-server.featuredeprecationdetails.deprecationtype.md b/docs/development/core/server/kibana-plugin-core-server.featuredeprecationdetails.deprecationtype.md new file mode 100644 index 0000000000000..b530874d3678b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.featuredeprecationdetails.deprecationtype.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [FeatureDeprecationDetails](./kibana-plugin-core-server.featuredeprecationdetails.md) > [deprecationType](./kibana-plugin-core-server.featuredeprecationdetails.deprecationtype.md) + +## FeatureDeprecationDetails.deprecationType property + +Signature: + +```typescript +deprecationType?: 'feature' | undefined; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.featuredeprecationdetails.md b/docs/development/core/server/kibana-plugin-core-server.featuredeprecationdetails.md new file mode 100644 index 0000000000000..bed3356e36129 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.featuredeprecationdetails.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [FeatureDeprecationDetails](./kibana-plugin-core-server.featuredeprecationdetails.md) + +## FeatureDeprecationDetails interface + + +Signature: + +```typescript +export interface FeatureDeprecationDetails extends BaseDeprecationDetails +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [deprecationType](./kibana-plugin-core-server.featuredeprecationdetails.deprecationtype.md) | 'feature' | undefined | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index 89203cb94d573..20b4b43776f21 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -53,9 +53,11 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthRedirectedParams](./kibana-plugin-core-server.authredirectedparams.md) | Result of auth redirection. | | [AuthResultParams](./kibana-plugin-core-server.authresultparams.md) | Result of successful authentication. | | [AuthToolkit](./kibana-plugin-core-server.authtoolkit.md) | A tool set defining an outcome of Auth interceptor for incoming request. | +| [BaseDeprecationDetails](./kibana-plugin-core-server.basedeprecationdetails.md) | Base properties shared by all types of deprecations | | [Capabilities](./kibana-plugin-core-server.capabilities.md) | The read-only set of capabilities available for the current UI session. Capabilities are simple key-value pairs of (string, boolean), where the string denotes the capability ID, and the boolean is a flag indicating if the capability is enabled or disabled. | | [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | APIs to manage the [Capabilities](./kibana-plugin-core-server.capabilities.md) that will be used by the application.Plugins relying on capabilities to toggle some of their features should register them during the setup phase using the registerProvider method.Plugins having the responsibility to restrict capabilities depending on a given context should register their capabilities switcher using the registerSwitcher method.Refers to the methods documentation for complete description and examples. | | [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md) | APIs to access the application [Capabilities](./kibana-plugin-core-server.capabilities.md). | +| [ConfigDeprecationDetails](./kibana-plugin-core-server.configdeprecationdetails.md) | | | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | | [CorePreboot](./kibana-plugin-core-server.corepreboot.md) | Context passed to the setup method of preboot plugins. | | [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the setup method of standard plugins. | @@ -65,7 +67,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [CustomHttpResponseOptions](./kibana-plugin-core-server.customhttpresponseoptions.md) | HTTP response parameters for a response with adjustable status code. | | [DeleteDocumentResponse](./kibana-plugin-core-server.deletedocumentresponse.md) | | | [DeprecationsClient](./kibana-plugin-core-server.deprecationsclient.md) | Server-side client that provides access to fetch all Kibana deprecations | -| [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) | | | [DeprecationSettings](./kibana-plugin-core-server.deprecationsettings.md) | UiSettings deprecation field options. | | [DeprecationsServiceSetup](./kibana-plugin-core-server.deprecationsservicesetup.md) | The deprecations service provides a way for the Kibana platform to communicate deprecated features and configs with its users. These deprecations are only communicated if the deployment is using these features. Allowing for a user tailored experience for upgrading the stack version.The Deprecation service is consumed by the upgrade assistant to assist with the upgrade experience.If a deprecated feature can be resolved without manual user intervention. Using correctiveActions.api allows the Upgrade Assistant to use this api to correct the deprecation upon a user trigger. | | [DiscoveredPlugin](./kibana-plugin-core-server.discoveredplugin.md) | Small container object used to expose information about discovered plugins that may or may not have been started. | @@ -77,6 +78,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters | | [ExecutionContextSetup](./kibana-plugin-core-server.executioncontextsetup.md) | | | [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. | +| [FeatureDeprecationDetails](./kibana-plugin-core-server.featuredeprecationdetails.md) | | | [GetDeprecationsContext](./kibana-plugin-core-server.getdeprecationscontext.md) | | | [GetResponse](./kibana-plugin-core-server.getresponse.md) | | | [HttpAuth](./kibana-plugin-core-server.httpauth.md) | | @@ -246,6 +248,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [AuthResult](./kibana-plugin-core-server.authresult.md) | | | [CapabilitiesProvider](./kibana-plugin-core-server.capabilitiesprovider.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | | [CapabilitiesSwitcher](./kibana-plugin-core-server.capabilitiesswitcher.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) | +| [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) | | | [DestructiveRouteMethod](./kibana-plugin-core-server.destructiveroutemethod.md) | Set of HTTP methods changing the state of the server. | | [ElasticsearchClient](./kibana-plugin-core-server.elasticsearchclient.md) | Client used to query the elasticsearch cluster. | | [ElasticsearchClientConfig](./kibana-plugin-core-server.elasticsearchclientconfig.md) | Configuration options to be used to create a [cluster client](./kibana-plugin-core-server.iclusterclient.md) using the [createClient API](./kibana-plugin-core-server.elasticsearchservicestart.createclient.md) | diff --git a/packages/kbn-config/src/config_service.test.ts b/packages/kbn-config/src/config_service.test.ts index e8fd7ab187596..4a8164b100626 100644 --- a/packages/kbn-config/src/config_service.test.ts +++ b/packages/kbn-config/src/config_service.test.ts @@ -382,6 +382,7 @@ test('logs deprecation if schema is not present and "enabled" is used', async () "foo", Array [ Object { + "configPath": "foo.enabled", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"foo.enabled\\" from the Kibana config file, CLI flag, or environment variable (in Docker only) before upgrading to 8.0.0.", @@ -452,10 +453,12 @@ test('logs deprecation warning during validation', async () => { mockApplyDeprecations.mockImplementationOnce((config, deprecations, createAddDeprecation) => { const addDeprecation = createAddDeprecation!(''); addDeprecation({ + configPath: 'test1', message: 'some deprecation message', correctiveActions: { manualSteps: ['do X'] }, }); addDeprecation({ + configPath: 'test2', message: 'another deprecation message', correctiveActions: { manualSteps: ['do Y'] }, }); @@ -521,11 +524,13 @@ test('does not log warnings for silent deprecations during validation', async () .mockImplementationOnce((config, deprecations, createAddDeprecation) => { const addDeprecation = createAddDeprecation!(''); addDeprecation({ + configPath: 'test1', message: 'some deprecation message', correctiveActions: { manualSteps: ['do X'] }, silent: true, }); addDeprecation({ + configPath: 'test2', message: 'another deprecation message', correctiveActions: { manualSteps: ['do Y'] }, }); @@ -534,6 +539,7 @@ test('does not log warnings for silent deprecations during validation', async () .mockImplementationOnce((config, deprecations, createAddDeprecation) => { const addDeprecation = createAddDeprecation!(''); addDeprecation({ + configPath: 'silent', message: 'I am silent', silent: true, correctiveActions: { manualSteps: ['do Z'] }, @@ -617,6 +623,7 @@ describe('getHandledDeprecatedConfigs', () => { deprecations.forEach((deprecation) => { const addDeprecation = createAddDeprecation!(deprecation.path); addDeprecation({ + configPath: 'test1', message: `some deprecation message`, documentationUrl: 'some-url', correctiveActions: { manualSteps: ['do X'] }, @@ -633,6 +640,7 @@ describe('getHandledDeprecatedConfigs', () => { "base", Array [ Object { + "configPath": "test1", "correctiveActions": Object { "manualSteps": Array [ "do X", diff --git a/packages/kbn-config/src/config_service.ts b/packages/kbn-config/src/config_service.ts index d99d4bd410743..f087b3fff8612 100644 --- a/packages/kbn-config/src/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -184,6 +184,7 @@ export class ConfigService { if (validatedConfig?.enabled === undefined && isEnabled !== undefined) { const deprecationPath = pathToString(enabledPath); const deprecatedConfigDetails: DeprecatedConfigDetails = { + configPath: deprecationPath, title: `Setting "${deprecationPath}" is deprecated`, message: `Configuring "${deprecationPath}" is deprecated and will be removed in 8.0.0.`, correctiveActions: { diff --git a/packages/kbn-config/src/deprecation/deprecation_factory.test.ts b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts index 415c8fb9f0610..d9fe90ff711ed 100644 --- a/packages/kbn-config/src/deprecation/deprecation_factory.test.ts +++ b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts @@ -43,6 +43,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"myplugin.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only) before upgrading to 8.0.0.", @@ -79,6 +80,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.section.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"myplugin.section.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only) before upgrading to 8.0.0.", @@ -134,6 +136,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"myplugin.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only) before upgrading to 8.0.0.", @@ -197,6 +200,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Replace \\"myplugin.deprecated\\" with \\"myplugin.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).", @@ -254,6 +258,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.oldsection.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Replace \\"myplugin.oldsection.deprecated\\" with \\"myplugin.newsection.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).", @@ -286,6 +291,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Make sure \\"myplugin.renamed\\" contains the correct value in the config file, CLI flag, or environment variable (in Docker only).", @@ -331,6 +337,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Replace \\"myplugin.deprecated\\" with \\"myplugin.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).", @@ -373,6 +380,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "oldplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Replace \\"oldplugin.deprecated\\" with \\"newplugin.renamed\\" in the Kibana config file, CLI flag, or environment variable (in Docker only).", @@ -427,6 +435,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Make sure \\"myplugin.renamed\\" contains the correct value in the config file, CLI flag, or environment variable (in Docker only).", @@ -461,6 +470,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"myplugin.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only).", @@ -494,6 +504,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.section.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"myplugin.section.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only).", @@ -546,6 +557,7 @@ describe('DeprecationFactory', () => { Array [ Array [ Object { + "configPath": "myplugin.deprecated", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"myplugin.deprecated\\" from the Kibana config file, CLI flag, or environment variable (in Docker only).", diff --git a/packages/kbn-config/src/deprecation/deprecation_factory.ts b/packages/kbn-config/src/deprecation/deprecation_factory.ts index 1d61733715bd9..ea4db280e915b 100644 --- a/packages/kbn-config/src/deprecation/deprecation_factory.ts +++ b/packages/kbn-config/src/deprecation/deprecation_factory.ts @@ -37,6 +37,7 @@ const _deprecate = ( return; } addDeprecation({ + configPath: fullPath, title: getDeprecationTitle(fullPath), message: i18n.translate('kbnConfig.deprecations.deprecatedSettingMessage', { defaultMessage: 'Configuring "{fullPath}" is deprecated and will be removed in {removeBy}.', @@ -73,6 +74,7 @@ const _rename = ( const newValue = get(config, fullNewPath); if (newValue === undefined) { addDeprecation({ + configPath: fullOldPath, title: getDeprecationTitle(fullOldPath), message: i18n.translate('kbnConfig.deprecations.replacedSettingMessage', { defaultMessage: `Setting "{fullOldPath}" has been replaced by "{fullNewPath}"`, @@ -95,6 +97,7 @@ const _rename = ( }; } else { addDeprecation({ + configPath: fullOldPath, title: getDeprecationTitle(fullOldPath), message: i18n.translate('kbnConfig.deprecations.conflictSettingMessage', { defaultMessage: @@ -135,6 +138,7 @@ const _unused = ( return; } addDeprecation({ + configPath: fullPath, title: getDeprecationTitle(fullPath), message: i18n.translate('kbnConfig.deprecations.unusedSettingMessage', { defaultMessage: 'You no longer need to configure "{fullPath}".', diff --git a/packages/kbn-config/src/deprecation/types.ts b/packages/kbn-config/src/deprecation/types.ts index 12b561aa2b1b9..f5bb240f5cc43 100644 --- a/packages/kbn-config/src/deprecation/types.ts +++ b/packages/kbn-config/src/deprecation/types.ts @@ -20,6 +20,8 @@ export type AddConfigDeprecation = (details: DeprecatedConfigDetails) => void; * @public */ export interface DeprecatedConfigDetails { + /** The path of the deprecated config setting */ + configPath: string; /** The title to be displayed for the deprecation. */ title?: string; /** The message to be displayed for the deprecation. */ @@ -30,7 +32,7 @@ export interface DeprecatedConfigDetails { * - critical: needs to be addressed before upgrade. */ level?: 'warning' | 'critical'; - /** (optional) set false to prevent the config service from logging the deprecation message. */ + /** (optional) set to `true` to prevent the config service from logging the deprecation message. */ silent?: boolean; /** (optional) link to the documentation for more details on the deprecation. */ documentationUrl?: string; diff --git a/packages/kbn-es-query/src/es_query/build_es_query.test.ts b/packages/kbn-es-query/src/es_query/build_es_query.test.ts index aca40632960f8..8fd884f65fa0d 100644 --- a/packages/kbn-es-query/src/es_query/build_es_query.test.ts +++ b/packages/kbn-es-query/src/es_query/build_es_query.test.ts @@ -12,13 +12,14 @@ import { luceneStringToDsl } from './lucene_string_to_dsl'; import { decorateQuery } from './decorate_query'; import { MatchAllFilter, Query } from '../filters'; import { fields } from '../filters/stubs'; -import { IndexPatternBase } from './types'; +import { DataViewBase } from './types'; jest.mock('../kuery/grammar'); describe('build query', () => { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { fields, + title: 'dataView', }; describe('buildEsQuery', () => { diff --git a/packages/kbn-es-query/src/es_query/from_filters.test.ts b/packages/kbn-es-query/src/es_query/from_filters.test.ts index eacf775194bb8..67de9e4d88736 100644 --- a/packages/kbn-es-query/src/es_query/from_filters.test.ts +++ b/packages/kbn-es-query/src/es_query/from_filters.test.ts @@ -9,11 +9,12 @@ import { buildQueryFromFilters } from './from_filters'; import { ExistsFilter, Filter, MatchAllFilter } from '../filters'; import { fields } from '../filters/stubs'; -import { IndexPatternBase } from './types'; +import { DataViewBase } from './types'; describe('build query', () => { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { fields, + title: 'dataView', }; describe('buildQueryFromFilters', () => { diff --git a/packages/kbn-es-query/src/es_query/from_kuery.test.ts b/packages/kbn-es-query/src/es_query/from_kuery.test.ts index 2458013854393..443b44dff5819 100644 --- a/packages/kbn-es-query/src/es_query/from_kuery.test.ts +++ b/packages/kbn-es-query/src/es_query/from_kuery.test.ts @@ -9,14 +9,15 @@ import { buildQueryFromKuery } from './from_kuery'; import { fromKueryExpression, toElasticsearchQuery } from '../kuery'; import { fields } from '../filters/stubs'; -import { IndexPatternBase } from './types'; +import { DataViewBase } from './types'; import { Query } from '..'; jest.mock('../kuery/grammar'); describe('build query', () => { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { fields, + title: 'dataView', }; describe('buildQueryFromKuery', () => { diff --git a/packages/kbn-es-query/src/es_query/handle_nested_filter.test.ts b/packages/kbn-es-query/src/es_query/handle_nested_filter.test.ts index 333f4bad54a46..2b9fbbe5ece04 100644 --- a/packages/kbn-es-query/src/es_query/handle_nested_filter.test.ts +++ b/packages/kbn-es-query/src/es_query/handle_nested_filter.test.ts @@ -9,12 +9,13 @@ import { handleNestedFilter } from './handle_nested_filter'; import { fields } from '../filters/stubs'; import { buildPhraseFilter, buildQueryFilter } from '../filters'; -import { IndexPatternBase } from './types'; +import { DataViewBase } from './types'; describe('handleNestedFilter', function () { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { id: 'logstash-*', fields, + title: 'dataView', }; it("should return the filter's query wrapped in nested query if the target field is nested", () => { diff --git a/packages/kbn-es-query/src/es_query/types.ts b/packages/kbn-es-query/src/es_query/types.ts index 3a5893d20ef25..75ea320b3431f 100644 --- a/packages/kbn-es-query/src/es_query/types.ts +++ b/packages/kbn-es-query/src/es_query/types.ts @@ -65,7 +65,7 @@ export type IndexPatternFieldBase = DataViewFieldBase; export interface DataViewBase { fields: DataViewFieldBase[]; id?: string; - title?: string; + title: string; } /** diff --git a/packages/kbn-es-query/src/filters/build_filters/build_filter.test.ts b/packages/kbn-es-query/src/filters/build_filters/build_filter.test.ts index 762af90063070..761d0341f1eae 100644 --- a/packages/kbn-es-query/src/filters/build_filters/build_filter.test.ts +++ b/packages/kbn-es-query/src/filters/build_filters/build_filter.test.ts @@ -7,13 +7,14 @@ */ import { buildFilter, FilterStateStore, FILTERS } from '.'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import { fields as stubFields } from '../stubs'; describe('buildFilter', () => { - const stubIndexPattern: IndexPatternBase = { + const stubIndexPattern: DataViewBase = { id: 'logstash-*', fields: stubFields, + title: 'dataView', }; it('should build phrase filters', () => { diff --git a/packages/kbn-es-query/src/filters/build_filters/exists_filter.test.ts b/packages/kbn-es-query/src/filters/build_filters/exists_filter.test.ts index 35a2a5c3d6057..81aab2c300243 100644 --- a/packages/kbn-es-query/src/filters/build_filters/exists_filter.test.ts +++ b/packages/kbn-es-query/src/filters/build_filters/exists_filter.test.ts @@ -6,13 +6,14 @@ * Side Public License, v 1. */ -import { IndexPatternBase } from '../../es_query'; +import { DataViewBase } from '../../es_query'; import { buildExistsFilter, getExistsFilterField } from './exists_filter'; import { fields } from '../stubs/fields.mocks'; describe('exists filter', function () { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { fields, + title: 'dataView', }; describe('getExistsFilterField', function () { diff --git a/packages/kbn-es-query/src/filters/build_filters/get_filter_field.test.ts b/packages/kbn-es-query/src/filters/build_filters/get_filter_field.test.ts index ca67a439757de..06803716aac2f 100644 --- a/packages/kbn-es-query/src/filters/build_filters/get_filter_field.test.ts +++ b/packages/kbn-es-query/src/filters/build_filters/get_filter_field.test.ts @@ -9,13 +9,14 @@ import { buildPhraseFilter } from './phrase_filter'; import { buildQueryFilter } from './query_string_filter'; import { getFilterField } from './get_filter_field'; -import { IndexPatternBase } from '../../es_query'; +import { DataViewBase } from '../../es_query'; import { fields } from '../stubs/fields.mocks'; describe('getFilterField', function () { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { id: 'logstash-*', fields, + title: 'dataView', }; it('should return the field name from known filter types that target a specific field', () => { diff --git a/packages/kbn-es-query/src/filters/build_filters/phrase_filter.test.ts b/packages/kbn-es-query/src/filters/build_filters/phrase_filter.test.ts index f23dfde12d977..13f18ad0cc7ea 100644 --- a/packages/kbn-es-query/src/filters/build_filters/phrase_filter.test.ts +++ b/packages/kbn-es-query/src/filters/build_filters/phrase_filter.test.ts @@ -13,16 +13,17 @@ import { PhraseFilter, } from './phrase_filter'; import { fields, getField } from '../stubs'; -import { IndexPatternBase } from '../../es_query'; +import { DataViewBase } from '../../es_query'; import { estypes } from '@elastic/elasticsearch'; describe('Phrase filter builder', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { id: 'id', fields, + title: 'dataView', }; }); @@ -151,8 +152,9 @@ describe('buildInlineScriptForPhraseFilter', () => { }); describe('getPhraseFilterField', function () { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { fields, + title: 'dataView', }; it('should return the name of the field a phrase query is targeting', () => { diff --git a/packages/kbn-es-query/src/filters/build_filters/phrases_filter.test.ts b/packages/kbn-es-query/src/filters/build_filters/phrases_filter.test.ts index c3e7fd37e1ff6..1a125e98f9645 100644 --- a/packages/kbn-es-query/src/filters/build_filters/phrases_filter.test.ts +++ b/packages/kbn-es-query/src/filters/build_filters/phrases_filter.test.ts @@ -6,13 +6,14 @@ * Side Public License, v 1. */ -import { IndexPatternBase } from '../../es_query'; +import { DataViewBase } from '../../es_query'; import { buildPhrasesFilter, getPhrasesFilterField } from './phrases_filter'; import { fields } from '../stubs'; describe('phrases filter', function () { - const indexPattern: IndexPatternBase = { + const indexPattern: DataViewBase = { fields, + title: 'dataView', }; describe('getPhrasesFilterField', function () { diff --git a/packages/kbn-es-query/src/kuery/ast/ast.test.ts b/packages/kbn-es-query/src/kuery/ast/ast.test.ts index edb9150ea00d5..0e4366d6e21d1 100644 --- a/packages/kbn-es-query/src/kuery/ast/ast.test.ts +++ b/packages/kbn-es-query/src/kuery/ast/ast.test.ts @@ -8,18 +8,19 @@ import { fromKueryExpression, fromLiteralExpression, toElasticsearchQuery } from './ast'; import { nodeTypes } from '../node_types'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import { KueryNode } from '../types'; import { fields } from '../../filters/stubs'; jest.mock('../grammar'); describe('kuery AST API', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/and.test.ts b/packages/kbn-es-query/src/kuery/functions/and.test.ts index 239342bdc0a1c..07e431791b37a 100644 --- a/packages/kbn-es-query/src/kuery/functions/and.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/and.test.ts @@ -10,7 +10,7 @@ import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; import * as ast from '../ast'; import * as and from './and'; -import { IndexPatternBase } from '../../es_query'; +import { DataViewBase } from '../../es_query'; jest.mock('../grammar'); @@ -19,11 +19,12 @@ const childNode2 = nodeTypes.function.buildNode('is', 'extension', 'jpg'); describe('kuery functions', () => { describe('and', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/exists.test.ts b/packages/kbn-es-query/src/kuery/functions/exists.test.ts index 0941e478e816b..ed368d6af2bfd 100644 --- a/packages/kbn-es-query/src/kuery/functions/exists.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/exists.test.ts @@ -8,7 +8,7 @@ import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; jest.mock('../grammar'); @@ -17,11 +17,12 @@ import * as exists from './exists'; describe('kuery functions', () => { describe('exists', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts index 45e6474b22986..9c4a33f50020f 100644 --- a/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_bounding_box.test.ts @@ -9,7 +9,7 @@ import { get } from 'lodash'; import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import * as geoBoundingBox from './geo_bounding_box'; import { JsonObject } from '@kbn/utility-types'; @@ -29,11 +29,12 @@ const params = { describe('kuery functions', () => { describe('geoBoundingBox', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts b/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts index 2d1d6ed6cd6de..5a96f6f3cb03d 100644 --- a/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/geo_polygon.test.ts @@ -8,7 +8,7 @@ import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import * as geoPolygon from './geo_polygon'; @@ -31,11 +31,12 @@ const points = [ describe('kuery functions', () => { describe('geoPolygon', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/is.test.ts b/packages/kbn-es-query/src/kuery/functions/is.test.ts index bc1bbb2508438..fbc6011331dbb 100644 --- a/packages/kbn-es-query/src/kuery/functions/is.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/is.test.ts @@ -10,18 +10,19 @@ import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; import * as is from './is'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import { estypes } from '@elastic/elasticsearch'; jest.mock('../grammar'); describe('kuery functions', () => { describe('is', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/nested.test.ts b/packages/kbn-es-query/src/kuery/functions/nested.test.ts index 7b6b7b695db2d..da7bfd2dce2d8 100644 --- a/packages/kbn-es-query/src/kuery/functions/nested.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/nested.test.ts @@ -8,7 +8,7 @@ import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import * as ast from '../ast'; @@ -20,11 +20,12 @@ const childNode = nodeTypes.function.buildNode('is', 'child', 'foo'); describe('kuery functions', () => { describe('nested', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/not.test.ts b/packages/kbn-es-query/src/kuery/functions/not.test.ts index af1e8108b40f5..96af4bc693621 100644 --- a/packages/kbn-es-query/src/kuery/functions/not.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/not.test.ts @@ -8,7 +8,7 @@ import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import * as ast from '../ast'; import * as not from './not'; @@ -19,11 +19,12 @@ const childNode = nodeTypes.function.buildNode('is', 'extension', 'jpg'); describe('kuery functions', () => { describe('not', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/or.test.ts b/packages/kbn-es-query/src/kuery/functions/or.test.ts index eb7db62b22162..63fde9e802401 100644 --- a/packages/kbn-es-query/src/kuery/functions/or.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/or.test.ts @@ -8,7 +8,7 @@ import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import * as ast from '../ast'; @@ -20,11 +20,12 @@ const childNode2 = nodeTypes.function.buildNode('is', 'extension', 'jpg'); describe('kuery functions', () => { describe('or', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/range.test.ts b/packages/kbn-es-query/src/kuery/functions/range.test.ts index 42005a3fe702d..c541b26ce176f 100644 --- a/packages/kbn-es-query/src/kuery/functions/range.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/range.test.ts @@ -9,7 +9,7 @@ import { get } from 'lodash'; import { nodeTypes } from '../node_types'; import { fields } from '../../filters/stubs'; -import { IndexPatternBase } from '../..'; +import { DataViewBase } from '../..'; import { RangeFilterParams } from '../../filters'; import * as range from './range'; @@ -18,11 +18,12 @@ jest.mock('../grammar'); describe('kuery functions', () => { describe('range', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.test.ts b/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.test.ts index dccfc5d1c463a..046ffdb152e42 100644 --- a/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.test.ts +++ b/packages/kbn-es-query/src/kuery/functions/utils/get_full_field_name_node.test.ts @@ -8,17 +8,18 @@ import { nodeTypes } from '../../node_types'; import { fields } from '../../../filters/stubs'; -import { IndexPatternBase } from '../../..'; +import { DataViewBase } from '../../..'; import { getFullFieldNameNode } from './get_full_field_name_node'; jest.mock('../../grammar'); describe('getFullFieldNameNode', function () { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es-query/src/kuery/node_types/function.test.ts b/packages/kbn-es-query/src/kuery/node_types/function.test.ts index 5df6ba1916046..754bf170c5062 100644 --- a/packages/kbn-es-query/src/kuery/node_types/function.test.ts +++ b/packages/kbn-es-query/src/kuery/node_types/function.test.ts @@ -10,18 +10,19 @@ import { nodeTypes } from './index'; import { buildNode, buildNodeWithArgumentNodes, toElasticsearchQuery } from './function'; import { toElasticsearchQuery as isFunctionToElasticsearchQuery } from '../functions/is'; -import { IndexPatternBase } from '../../es_query'; +import { DataViewBase } from '../../es_query'; import { fields } from '../../filters/stubs/fields.mocks'; jest.mock('../grammar'); describe('kuery node types', () => { describe('function', () => { - let indexPattern: IndexPatternBase; + let indexPattern: DataViewBase; beforeEach(() => { indexPattern = { fields, + title: 'dataView', }; }); diff --git a/packages/kbn-es/src/utils/build_snapshot.js b/packages/kbn-es/src/utils/build_snapshot.js index c60ef61e98538..ec26ba69e658b 100644 --- a/packages/kbn-es/src/utils/build_snapshot.js +++ b/packages/kbn-es/src/utils/build_snapshot.js @@ -26,6 +26,7 @@ const onceEvent = (emitter, event) => new Promise((resolve) => emitter.once(even * @returns {Object} containing archive and optional plugins * * Gradle tasks: + * $ ./gradlew tasks --all | grep 'distribution.*assemble\s' * :distribution:archives:darwin-tar:assemble * :distribution:archives:linux-tar:assemble * :distribution:archives:windows-zip:assemble diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index aaa697199f0a4..e5813f8f38139 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -108,7 +108,7 @@ pageLoadAssetSize: data: 491273 dataViews: 42532 fieldFormats: 65209 - kibanaReact: 99422 + kibanaReact: 84422 uiActions: 35121 dataEnhanced: 24980 embeddable: 87309 diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 2da4370242e6c..f39ad9d5f8cde 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -736,10 +736,9 @@ export interface DocLinksStart { // Warning: (ae-forgotten-export) The symbol "DeprecationsDetails" needs to be exported by the entry point index.d.ts // // @internal (undocumented) -export interface DomainDeprecationDetails extends DeprecationsDetails { - // (undocumented) +export type DomainDeprecationDetails = DeprecationsDetails & { domainId: string; -} +}; export { EnvironmentMode } diff --git a/src/core/public/rendering/_base.scss b/src/core/public/rendering/_base.scss index 18e564abf822f..32a297a4066d9 100644 --- a/src/core/public/rendering/_base.scss +++ b/src/core/public/rendering/_base.scss @@ -42,6 +42,12 @@ #app-fixed-viewport { top: $headerHeight; } + + .kbnStickyMenu { + position: sticky; + max-height: calc(100vh - #{$headerHeight + $euiSize}); + top: $headerHeight + $euiSize; + } } .kbnBody { diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 6909375408fb4..1cf67f479f9b3 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -12,6 +12,7 @@ import { ConfigDeprecationProvider, ConfigDeprecation } from '@kbn/config'; const kibanaPathConf: ConfigDeprecation = (settings, fromPath, addDeprecation) => { if (process.env?.KIBANA_PATH_CONF) { addDeprecation({ + configPath: 'env.KIBANA_PATH_CONF', message: `Environment variable "KIBANA_PATH_CONF" is deprecated. It has been replaced with "KBN_PATH_CONF" pointing to a config folder`, correctiveActions: { manualSteps: [ @@ -25,6 +26,7 @@ const kibanaPathConf: ConfigDeprecation = (settings, fromPath, addDeprecation) = const configPathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { if (process.env?.CONFIG_PATH) { addDeprecation({ + configPath: 'env.CONFIG_PATH', message: `Environment variable "CONFIG_PATH" is deprecated. It has been replaced with "KBN_PATH_CONF" pointing to a config folder`, correctiveActions: { manualSteps: ['Use "KBN_PATH_CONF" instead of "CONFIG_PATH" to point to a config folder.'], @@ -36,6 +38,7 @@ const configPathDeprecation: ConfigDeprecation = (settings, fromPath, addDepreca const dataPathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { if (process.env?.DATA_PATH) { addDeprecation({ + configPath: 'env.DATA_PATH', message: `Environment variable "DATA_PATH" will be removed. It has been replaced with kibana.yml setting "path.data"`, correctiveActions: { manualSteps: [ @@ -49,6 +52,8 @@ const dataPathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecati const rewriteBasePathDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { if (settings.server?.basePath && !settings.server?.rewriteBasePath) { addDeprecation({ + configPath: 'server.basePath', + title: 'Setting "server.rewriteBasePath" should be set when using "server.basePath"', message: 'You should set server.basePath along with server.rewriteBasePath. Starting in 7.0, Kibana ' + 'will expect that all requests start with server.basePath rather than expecting you to rewrite ' + @@ -69,6 +74,8 @@ const rewriteCorsSettings: ConfigDeprecation = (settings, fromPath, addDeprecati const corsSettings = settings.server?.cors; if (typeof corsSettings === 'boolean') { addDeprecation({ + configPath: 'server.cors', + title: 'Setting "server.cors" is deprecated', message: '"server.cors" is deprecated and has been replaced by "server.cors.enabled"', correctiveActions: { manualSteps: [ @@ -105,6 +112,7 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecati value: [...parsed].map(([policy, sourceList]) => { if (sourceList.find((source) => source.includes(NONCE_STRING))) { addDeprecation({ + configPath: 'csp.rules', message: `csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in ${policy}`, correctiveActions: { manualSteps: [`Replace {nonce} syntax with 'self' in ${policy}`], @@ -123,6 +131,7 @@ const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecati !sourceList.find((source) => source.includes(SELF_STRING)) ) { addDeprecation({ + configPath: 'csp.rules', message: `csp.rules must contain the 'self' source. Automatically adding to ${policy}.`, correctiveActions: { manualSteps: [`Add 'self' source to ${policy}.`], @@ -146,6 +155,7 @@ const mapManifestServiceUrlDeprecation: ConfigDeprecation = ( ) => { if (settings.map?.manifestServiceUrl) { addDeprecation({ + configPath: 'map.manifestServiceUrl', message: 'You should no longer use the map.manifestServiceUrl setting in kibana.yml to configure the location ' + 'of the Elastic Maps Service settings. These settings have moved to the "map.emsTileApiUrl" and ' + @@ -164,6 +174,7 @@ const mapManifestServiceUrlDeprecation: ConfigDeprecation = ( const serverHostZeroDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { if (settings.server?.host === '0') { addDeprecation({ + configPath: 'server.host', message: 'Support for setting server.host to "0" in kibana.yml is deprecated and will be removed in Kibana version 8.0.0. ' + 'Instead use "0.0.0.0" to bind to all interfaces.', @@ -194,6 +205,7 @@ const opsLoggingEventDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.events?.ops) { addDeprecation({ + configPath: 'logging.events.ops', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`, title: i18n.translate('core.deprecations.loggingEventsOps.deprecationTitle', { defaultMessage: `Setting "logging.events.ops" is deprecated`, @@ -224,6 +236,7 @@ const requestLoggingEventDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.events?.request) { addDeprecation({ + configPath: 'logging.events.request', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`, title: i18n.translate('core.deprecations.loggingEventsRequest.deprecationTitle', { defaultMessage: `Setting "logging.events.request" is deprecated`, @@ -254,6 +267,7 @@ const responseLoggingEventDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.events?.response) { addDeprecation({ + configPath: 'logging.events.response', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`, title: i18n.translate('core.deprecations.loggingEventsResponse.deprecationTitle', { defaultMessage: `Setting "logging.events.response" is deprecated`, @@ -284,6 +298,7 @@ const timezoneLoggingDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.timezone) { addDeprecation({ + configPath: 'logging.timezone', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingtimezone`, title: i18n.translate('core.deprecations.loggingTimezone.deprecationTitle', { defaultMessage: `Setting "logging.timezone" is deprecated`, @@ -314,6 +329,7 @@ const destLoggingDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.dest) { addDeprecation({ + configPath: 'logging.dest', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingdest`, title: i18n.translate('core.deprecations.loggingDest.deprecationTitle', { defaultMessage: `Setting "logging.dest" is deprecated`, @@ -344,6 +360,7 @@ const quietLoggingDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.quiet) { addDeprecation({ + configPath: 'logging.quiet', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingquiet`, title: i18n.translate('core.deprecations.loggingQuiet.deprecationTitle', { defaultMessage: `Setting "logging.quiet" is deprecated`, @@ -373,6 +390,7 @@ const silentLoggingDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.silent) { addDeprecation({ + configPath: 'logging.silent', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingsilent`, title: i18n.translate('core.deprecations.loggingSilent.deprecationTitle', { defaultMessage: `Setting "logging.silent" is deprecated`, @@ -402,6 +420,7 @@ const verboseLoggingDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.verbose) { addDeprecation({ + configPath: 'logging.verbose', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingverbose`, title: i18n.translate('core.deprecations.loggingVerbose.deprecationTitle', { defaultMessage: `Setting "logging.verbose" is deprecated`, @@ -435,6 +454,7 @@ const jsonLoggingDeprecation: ConfigDeprecation = ( // ` legacyLoggingConfigSchema` returns `true` for the TTY check on `process.stdout.isTTY` if (settings.logging?.json && settings.env !== 'development') { addDeprecation({ + configPath: 'logging.json', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx`, title: i18n.translate('core.deprecations.loggingJson.deprecationTitle', { defaultMessage: `Setting "logging.json" is deprecated`, @@ -466,6 +486,7 @@ const logRotateDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.rotate) { addDeprecation({ + configPath: 'logging.rotate', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#rolling-file-appender`, title: i18n.translate('core.deprecations.loggingRotate.deprecationTitle', { defaultMessage: `Setting "logging.rotate" is deprecated`, @@ -496,6 +517,7 @@ const logEventsLogDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.events?.log) { addDeprecation({ + configPath: 'logging.events.log', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`, title: i18n.translate('core.deprecations.loggingEventsLog.deprecationTitle', { defaultMessage: `Setting "logging.events.log" is deprecated`, @@ -525,6 +547,7 @@ const logEventsErrorDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.events?.error) { addDeprecation({ + configPath: 'logging.events.error', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingevents`, title: i18n.translate('core.deprecations.loggingEventsError.deprecationTitle', { defaultMessage: `Setting "logging.events.error" is deprecated`, @@ -554,6 +577,7 @@ const logFilterDeprecation: ConfigDeprecation = ( ) => { if (settings.logging?.filter) { addDeprecation({ + configPath: 'logging.filter', documentationUrl: `https://github.com/elastic/kibana/blob/${branch}/src/core/server/logging/README.mdx#loggingfilter`, title: i18n.translate('core.deprecations.loggingFilter.deprecationTitle', { defaultMessage: `Setting "logging.filter" is deprecated`, diff --git a/src/core/server/csp/config.ts b/src/core/server/csp/config.ts index 16a2fa4e62894..6697eda62597b 100644 --- a/src/core/server/csp/config.ts +++ b/src/core/server/csp/config.ts @@ -137,6 +137,7 @@ export const config: ServiceConfigDescriptor = { const cspConfig = rawConfig[fromPath]; if (cspConfig?.rules) { addDeprecation({ + configPath: 'csp.rules', message: '`csp.rules` is deprecated in favor of directive specific configuration. Please use `csp.connect_src`, ' + '`csp.default_src`, `csp.font_src`, `csp.frame_ancestors`, `csp.frame_src`, `csp.img_src`, ' + diff --git a/src/core/server/deprecations/deprecation_config.ts b/src/core/server/deprecations/deprecation_config.ts new file mode 100644 index 0000000000000..fb5ff7e5957f5 --- /dev/null +++ b/src/core/server/deprecations/deprecation_config.ts @@ -0,0 +1,22 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { ServiceConfigDescriptor } from '../internal_types'; + +const configSchema = schema.object({ + // `deprecation.skip_deprecated_settings` is consistent with the equivalent ES feature and config property + skip_deprecated_settings: schema.arrayOf(schema.string(), { defaultValue: [] }), +}); + +export type DeprecationConfigType = TypeOf; + +export const config: ServiceConfigDescriptor = { + path: 'deprecation', + schema: configSchema, +}; diff --git a/src/core/server/deprecations/deprecations_factory.test.ts b/src/core/server/deprecations/deprecations_factory.test.ts index 85ef521538b15..9a67c6dc36dd2 100644 --- a/src/core/server/deprecations/deprecations_factory.test.ts +++ b/src/core/server/deprecations/deprecations_factory.test.ts @@ -7,20 +7,26 @@ */ import type { GetDeprecationsContext } from './types'; -import { DeprecationsFactory } from './deprecations_factory'; +import { DeprecationsFactory, DeprecationsFactoryConfig } from './deprecations_factory'; import { loggerMock } from '../logging/logger.mock'; +import { DeprecationsDetails } from './types'; describe('DeprecationsFactory', () => { - const logger = loggerMock.create(); + let logger: ReturnType; + let config: DeprecationsFactoryConfig; + beforeEach(() => { - loggerMock.clear(logger); + logger = loggerMock.create(); + config = { + ignoredConfigDeprecations: [], + }; }); describe('getRegistry', () => { const domainId = 'test-plugin'; it('creates a registry for a domainId', async () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + const deprecationsFactory = new DeprecationsFactory({ logger, config }); const registry = deprecationsFactory.getRegistry(domainId); expect(registry).toHaveProperty('registerDeprecations'); @@ -28,7 +34,7 @@ describe('DeprecationsFactory', () => { }); it('creates one registry for a domainId', async () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + const deprecationsFactory = new DeprecationsFactory({ logger, config }); const registry = deprecationsFactory.getRegistry(domainId); const sameRegistry = deprecationsFactory.getRegistry(domainId); @@ -36,7 +42,7 @@ describe('DeprecationsFactory', () => { }); it('returns a registered registry', () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + const deprecationsFactory = new DeprecationsFactory({ logger, config }); const mockRegistry = 'mock-reg'; const mockRegistries = { set: jest.fn(), @@ -61,7 +67,7 @@ describe('DeprecationsFactory', () => { } as unknown as GetDeprecationsContext; it('returns a flattened array of deprecations', async () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + const deprecationsFactory = new DeprecationsFactory({ logger, config }); const mockPluginDeprecationsInfo = [ { message: 'mockPlugin message', @@ -97,8 +103,8 @@ describe('DeprecationsFactory', () => { getDeprecations: jest.fn().mockResolvedValue(anotherMockPluginDeprecationsInfo), }); - const derpecations = await deprecationsFactory.getAllDeprecations(mockDependencies); - expect(derpecations).toStrictEqual( + const deprecations = await deprecationsFactory.getAllDeprecations(mockDependencies); + expect(deprecations).toStrictEqual( [ mockPluginDeprecationsInfo.map((info) => ({ ...info, domainId: 'mockPlugin' })), anotherMockPluginDeprecationsInfo.map((info) => ({ @@ -110,7 +116,7 @@ describe('DeprecationsFactory', () => { }); it(`returns a failure message for failed getDeprecations functions`, async () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + const deprecationsFactory = new DeprecationsFactory({ logger, config }); const domainId = 'mockPlugin'; const mockError = new Error(); @@ -142,7 +148,7 @@ describe('DeprecationsFactory', () => { }); it(`returns successful results even when some getDeprecations fail`, async () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + const deprecationsFactory = new DeprecationsFactory({ logger, config }); const mockPluginRegistry = deprecationsFactory.getRegistry('mockPlugin'); const anotherMockPluginRegistry = deprecationsFactory.getRegistry('anotherMockPlugin'); const mockError = new Error(); @@ -161,14 +167,14 @@ describe('DeprecationsFactory', () => { anotherMockPluginRegistry.registerDeprecations({ getDeprecations: jest.fn().mockRejectedValue(mockError), }); - const derpecations = await deprecationsFactory.getAllDeprecations(mockDependencies); + const deprecations = await deprecationsFactory.getAllDeprecations(mockDependencies); expect(logger.warn).toBeCalledTimes(1); expect(logger.warn).toBeCalledWith( `Failed to get deprecations info for plugin "anotherMockPlugin".`, mockError ); - expect(derpecations).toStrictEqual([ + expect(deprecations).toStrictEqual([ ...mockPluginDeprecationsInfo.map((info) => ({ ...info, domainId: 'mockPlugin' })), { domainId: 'anotherMockPlugin', @@ -181,6 +187,123 @@ describe('DeprecationsFactory', () => { }, ]); }); + + it('excludes config deprecations explicitly ignored via `ignoredConfigDeprecations`', async () => { + const deprecationsFactory = new DeprecationsFactory({ + logger, + config: { + ignoredConfigDeprecations: ['mockPlugin.foo', 'anotherMockPlugin.bar'], + }, + }); + const mockPluginDeprecationsInfo: DeprecationsDetails[] = [ + { + configPath: 'mockPlugin.foo', + title: 'mockPlugin.foo is deprecated', + message: 'mockPlugin.foo is deprecated and will be removed in a future Kibana version', + level: 'critical', + deprecationType: 'config', + correctiveActions: { + manualSteps: ['come on', 'do something'], + }, + }, + { + configPath: 'mockPlugin.bar', + title: 'mockPlugin.bar is deprecated', + message: 'mockPlugin.bar is deprecated and will be removed in a future Kibana version', + level: 'critical', + deprecationType: 'config', + correctiveActions: { + manualSteps: ['come on', 'do something'], + }, + }, + ]; + const anotherMockPluginDeprecationsInfo: DeprecationsDetails[] = [ + { + configPath: 'anotherMockPlugin.foo', + title: 'anotherMockPlugin.foo is deprecated', + message: + 'anotherMockPlugin.foo is deprecated and will be removed in a future Kibana version', + level: 'critical', + deprecationType: 'config', + correctiveActions: { + manualSteps: ['come on', 'do something'], + }, + }, + { + configPath: 'anotherMockPlugin.bar', + title: 'anotherMockPlugin.bar is deprecated', + message: + 'anotherMockPlugin.bar is deprecated and will be removed in a future Kibana version', + level: 'critical', + deprecationType: 'config', + correctiveActions: { + manualSteps: ['come on', 'do something'], + }, + }, + ]; + + const mockPluginRegistry = deprecationsFactory.getRegistry('mockPlugin'); + mockPluginRegistry.registerDeprecations({ + getDeprecations: jest.fn().mockResolvedValue(mockPluginDeprecationsInfo), + }); + + const anotherMockPluginRegistry = deprecationsFactory.getRegistry('anotherMockPlugin'); + anotherMockPluginRegistry.registerDeprecations({ + getDeprecations: jest.fn().mockResolvedValue(anotherMockPluginDeprecationsInfo), + }); + + const deprecations = await deprecationsFactory.getAllDeprecations(mockDependencies); + + expect(deprecations).toHaveLength(2); + expect(deprecations).toEqual([ + expect.objectContaining({ + configPath: 'mockPlugin.bar', + title: 'mockPlugin.bar is deprecated', + }), + expect.objectContaining({ + configPath: 'anotherMockPlugin.foo', + title: 'anotherMockPlugin.foo is deprecated', + }), + ]); + }); + + it('does not throw when configured with paths not matching any deprecation', async () => { + const deprecationsFactory = new DeprecationsFactory({ + logger, + config: { + ignoredConfigDeprecations: ['unknown.bar'], + }, + }); + const mockPluginDeprecationsInfo: DeprecationsDetails[] = [ + { + configPath: 'mockPlugin.foo', + title: 'mockPlugin.foo is deprecated', + message: 'mockPlugin.foo is deprecated and will be removed in a future Kibana version', + level: 'critical', + deprecationType: 'config', + correctiveActions: { + manualSteps: ['come on', 'do something'], + }, + }, + { + configPath: 'mockPlugin.bar', + title: 'mockPlugin.bar is deprecated', + message: 'mockPlugin.bar is deprecated and will be removed in a future Kibana version', + level: 'critical', + deprecationType: 'config', + correctiveActions: { + manualSteps: ['come on', 'do something'], + }, + }, + ]; + + const mockPluginRegistry = deprecationsFactory.getRegistry('mockPlugin'); + mockPluginRegistry.registerDeprecations({ + getDeprecations: jest.fn().mockResolvedValue(mockPluginDeprecationsInfo), + }); + + await expect(deprecationsFactory.getAllDeprecations(mockDependencies)).resolves.toBeDefined(); + }); }); describe('getDeprecations', () => { @@ -190,7 +313,7 @@ describe('DeprecationsFactory', () => { } as unknown as GetDeprecationsContext; it('returns a flattened array of DeprecationInfo', async () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + const deprecationsFactory = new DeprecationsFactory({ logger, config }); const deprecationsRegistry = deprecationsFactory.getRegistry('mockPlugin'); const deprecationsBody = [ { @@ -215,40 +338,62 @@ describe('DeprecationsFactory', () => { getDeprecations: jest.fn().mockResolvedValue(deprecationsBody), }); - const derpecations = await deprecationsFactory.getDeprecations( + const deprecations = await deprecationsFactory.getDeprecations( 'mockPlugin', mockDependencies ); - expect(derpecations).toStrictEqual( + expect(deprecations).toStrictEqual( deprecationsBody.flat().map((body) => ({ ...body, domainId: 'mockPlugin' })) ); }); - it('removes empty entries from the returned array', async () => { - const deprecationsFactory = new DeprecationsFactory({ logger }); + it('excludes config deprecations explicitly ignored via `ignoredConfigDeprecations`', async () => { + const deprecationsFactory = new DeprecationsFactory({ + logger, + config: { + ignoredConfigDeprecations: ['test.foo'], + }, + }); const deprecationsRegistry = deprecationsFactory.getRegistry('mockPlugin'); - const deprecationsBody = [ + const deprecationsBody: DeprecationsDetails[] = [ { - message: 'mockPlugin message', + configPath: 'test.foo', + title: 'test.foo is deprecated', + message: 'test.foo is deprecated and will be removed in a future Kibana version', level: 'critical', + deprecationType: 'config', correctiveActions: { - manualSteps: ['mockPlugin step 1', 'mockPlugin step 2'], + manualSteps: ['come on', 'do something'], + }, + }, + { + configPath: 'test.bar', + title: 'test.bar is deprecated', + message: 'test.bar is deprecated and will be removed in a future Kibana version', + level: 'critical', + deprecationType: 'config', + correctiveActions: { + manualSteps: ['come on', 'do something'], }, }, - [undefined], - undefined, ]; deprecationsRegistry.registerDeprecations({ getDeprecations: jest.fn().mockResolvedValue(deprecationsBody), }); - const derpecations = await deprecationsFactory.getDeprecations( + const deprecations = await deprecationsFactory.getDeprecations( 'mockPlugin', mockDependencies ); - expect(derpecations).toHaveLength(1); - expect(derpecations).toStrictEqual([{ ...deprecationsBody[0], domainId: 'mockPlugin' }]); + expect(deprecations).toHaveLength(1); + expect(deprecations[0]).toEqual( + expect.objectContaining({ + deprecationType: 'config', + configPath: 'test.bar', + title: 'test.bar is deprecated', + }) + ); }); }); }); diff --git a/src/core/server/deprecations/deprecations_factory.ts b/src/core/server/deprecations/deprecations_factory.ts index 9905f0b26b4f3..ad28af2db528e 100644 --- a/src/core/server/deprecations/deprecations_factory.ts +++ b/src/core/server/deprecations/deprecations_factory.ts @@ -17,13 +17,21 @@ import type { export interface DeprecationsFactoryDeps { logger: Logger; + config: DeprecationsFactoryConfig; +} + +export interface DeprecationsFactoryConfig { + ignoredConfigDeprecations: string[]; } export class DeprecationsFactory { private readonly registries: Map = new Map(); private readonly logger: Logger; - constructor({ logger }: DeprecationsFactoryDeps) { + private readonly config: DeprecationsFactoryConfig; + + constructor({ logger, config }: DeprecationsFactoryDeps) { this.logger = logger; + this.config = config; } public getRegistry = (domainId: string): DeprecationsRegistry => { @@ -41,7 +49,7 @@ export class DeprecationsFactory { dependencies: GetDeprecationsContext ): Promise => { const infoBody = await this.getDeprecationsBody(domainId, dependencies); - return this.createDeprecationInfo(domainId, infoBody).flat(); + return this.createDeprecationInfo(domainId, infoBody); }; public getAllDeprecations = async ( @@ -63,13 +71,10 @@ export class DeprecationsFactory { domainId: string, deprecationInfoBody: DeprecationsDetails[] ): DomainDeprecationDetails[] => { - return deprecationInfoBody - .flat() - .filter(Boolean) - .map((pluginDeprecation) => ({ - ...pluginDeprecation, - domainId, - })); + return deprecationInfoBody.map((pluginDeprecation) => ({ + ...pluginDeprecation, + domainId, + })); }; private getDeprecationsBody = async ( @@ -113,7 +118,7 @@ export class DeprecationsFactory { ]; } - return settledResult.value; + return filterIgnoredDeprecations(settledResult.value.flat(), this.config); }); } catch (err) { this.logger.warn(`Failed to get deprecations info for plugin "${domainId}".`, err); @@ -121,3 +126,15 @@ export class DeprecationsFactory { } }; } + +const filterIgnoredDeprecations = ( + deprecations: DeprecationsDetails[], + config: DeprecationsFactoryConfig +): DeprecationsDetails[] => { + return deprecations.filter((deprecation) => { + if (deprecation.deprecationType === 'config') { + return !config.ignoredConfigDeprecations.includes(deprecation.configPath); + } + return true; + }); +}; diff --git a/src/core/server/deprecations/deprecations_service.mock.ts b/src/core/server/deprecations/deprecations_service.mock.ts index 2f9c8bade6405..c274c4409cce2 100644 --- a/src/core/server/deprecations/deprecations_service.mock.ts +++ b/src/core/server/deprecations/deprecations_service.mock.ts @@ -50,7 +50,7 @@ const createDeprecationsServiceMock = () => { stop: jest.fn(), }; - mocked.setup.mockReturnValue(createInternalSetupContractMock()); + mocked.setup.mockResolvedValue(createInternalSetupContractMock()); return mocked; }; diff --git a/src/core/server/deprecations/deprecations_service.test.mocks.ts b/src/core/server/deprecations/deprecations_service.test.mocks.ts new file mode 100644 index 0000000000000..3174698725f96 --- /dev/null +++ b/src/core/server/deprecations/deprecations_service.test.mocks.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { mockDeprecationsFactory } from './deprecations_factory.mock'; + +export const mockedDeprecationFactoryInstance = mockDeprecationsFactory.create(); +export const DeprecationsFactoryMock = jest + .fn() + .mockImplementation(() => mockedDeprecationFactoryInstance); + +jest.doMock('./deprecations_factory', () => ({ + DeprecationsFactory: DeprecationsFactoryMock, +})); diff --git a/src/core/server/deprecations/deprecations_service.test.ts b/src/core/server/deprecations/deprecations_service.test.ts index 0067cff1d2306..256cfdaa666b3 100644 --- a/src/core/server/deprecations/deprecations_service.test.ts +++ b/src/core/server/deprecations/deprecations_service.test.ts @@ -6,42 +6,69 @@ * Side Public License, v 1. */ +import { DeprecationsFactoryMock } from './deprecations_service.test.mocks'; + /* eslint-disable dot-notation */ -import { DeprecationsService } from './deprecations_service'; +import { DeprecationsService, DeprecationsSetupDeps } from './deprecations_service'; import { httpServiceMock } from '../http/http_service.mock'; -import { mockRouter } from '../http/router/router.mock'; -import { savedObjectsClientMock, elasticsearchServiceMock } from '../mocks'; +import { savedObjectsClientMock, elasticsearchServiceMock, configServiceMock } from '../mocks'; import { mockCoreContext } from '../core_context.mock'; import { mockDeprecationsFactory } from './deprecations_factory.mock'; import { mockDeprecationsRegistry } from './deprecations_registry.mock'; describe('DeprecationsService', () => { - const coreContext = mockCoreContext.create(); - const http = httpServiceMock.createInternalSetupContract(); - const router = mockRouter.create(); - http.createRouter.mockReturnValue(router); - const deprecationsCoreSetupDeps = { http }; + let coreContext: ReturnType; + let http: ReturnType; + let router: ReturnType; + let deprecationsCoreSetupDeps: DeprecationsSetupDeps; + + beforeEach(() => { + const configService = configServiceMock.create({ + atPath: { skip_deprecated_settings: ['hello', 'world'] }, + }); + coreContext = mockCoreContext.create({ configService }); + http = httpServiceMock.createInternalSetupContract(); + router = httpServiceMock.createRouter(); + http.createRouter.mockReturnValue(router); + deprecationsCoreSetupDeps = { http }; + }); - beforeEach(() => jest.clearAllMocks()); + afterEach(() => { + jest.clearAllMocks(); + DeprecationsFactoryMock.mockClear(); + }); describe('#setup', () => { - it('registers routes', () => { + it('registers routes', async () => { const deprecationsService = new DeprecationsService(coreContext); - deprecationsService.setup(deprecationsCoreSetupDeps); - // Registers correct base api path + await deprecationsService.setup(deprecationsCoreSetupDeps); + // registers correct base api path expect(http.createRouter).toBeCalledWith('/api/deprecations'); // registers get route '/' expect(router.get).toHaveBeenCalledTimes(1); expect(router.get).toHaveBeenCalledWith({ path: '/', validate: false }, expect.any(Function)); }); - it('calls registerConfigDeprecationsInfo', () => { + it('calls registerConfigDeprecationsInfo', async () => { const deprecationsService = new DeprecationsService(coreContext); const mockRegisterConfigDeprecationsInfo = jest.fn(); deprecationsService['registerConfigDeprecationsInfo'] = mockRegisterConfigDeprecationsInfo; - deprecationsService.setup(deprecationsCoreSetupDeps); + await deprecationsService.setup(deprecationsCoreSetupDeps); expect(mockRegisterConfigDeprecationsInfo).toBeCalledTimes(1); }); + + it('creates DeprecationsFactory with the correct parameters', async () => { + const deprecationsService = new DeprecationsService(coreContext); + await deprecationsService.setup(deprecationsCoreSetupDeps); + + expect(DeprecationsFactoryMock).toHaveBeenCalledTimes(1); + expect(DeprecationsFactoryMock).toHaveBeenCalledWith({ + logger: expect.any(Object), + config: { + ignoredConfigDeprecations: ['hello', 'world'], + }, + }); + }); }); describe('#start', () => { @@ -51,7 +78,7 @@ describe('DeprecationsService', () => { const savedObjectsClient = savedObjectsClientMock.create(); const deprecationsService = new DeprecationsService(coreContext); - deprecationsService.setup(deprecationsCoreSetupDeps); + await deprecationsService.setup(deprecationsCoreSetupDeps); const start = deprecationsService.start(); const deprecationsClient = start.asScopedToClient(esClient, savedObjectsClient); @@ -73,6 +100,7 @@ describe('DeprecationsService', () => { 'testDomain', [ { + configPath: 'test', message: 'testMessage', documentationUrl: 'testDocUrl', correctiveActions: { @@ -100,6 +128,7 @@ describe('DeprecationsService', () => { expect(configDeprecations).toMatchInlineSnapshot(` Array [ Object { + "configPath": "test", "correctiveActions": Object { "manualSteps": Array [ "Using Kibana user management, change all users using the kibana_user role to the kibana_admin role.", @@ -124,6 +153,7 @@ describe('DeprecationsService', () => { 'testDomain', [ { + configPath: 'test', message: 'testMessage', level: 'warning', correctiveActions: { diff --git a/src/core/server/deprecations/deprecations_service.ts b/src/core/server/deprecations/deprecations_service.ts index cfc0aae443d1b..0c3fd75987aa6 100644 --- a/src/core/server/deprecations/deprecations_service.ts +++ b/src/core/server/deprecations/deprecations_service.ts @@ -6,11 +6,14 @@ * Side Public License, v 1. */ +import { take } from 'rxjs/operators'; + import { DeprecationsFactory } from './deprecations_factory'; import { DomainDeprecationDetails, RegisterDeprecationsConfig } from './types'; import { registerRoutes } from './routes'; - +import { config as deprecationConfig, DeprecationConfigType } from './deprecation_config'; import { CoreContext } from '../core_context'; +import { IConfigService } from '../config'; import { CoreService } from '../../types'; import { InternalHttpServiceSetup } from '../http'; import { Logger } from '../logging'; @@ -103,6 +106,7 @@ export interface DeprecationsServiceSetup { export interface DeprecationsClient { getAllDeprecations: () => Promise; } + export interface InternalDeprecationsServiceStart { /** * Creates a {@link DeprecationsClient} with provided SO client and ES client. @@ -129,22 +133,33 @@ export class DeprecationsService implements CoreService { private readonly logger: Logger; - private readonly deprecationsFactory: DeprecationsFactory; + private readonly configService: IConfigService; + private deprecationsFactory?: DeprecationsFactory; - constructor(private readonly coreContext: Pick) { + constructor(coreContext: Pick) { this.logger = coreContext.logger.get('deprecations-service'); - this.deprecationsFactory = new DeprecationsFactory({ - logger: this.logger, - }); + this.configService = coreContext.configService; } - public setup({ http }: DeprecationsSetupDeps): InternalDeprecationsServiceSetup { + public async setup({ http }: DeprecationsSetupDeps): Promise { this.logger.debug('Setting up Deprecations service'); - const deprecationsFactory = this.deprecationsFactory; + + const config = await this.configService + .atPath(deprecationConfig.path) + .pipe(take(1)) + .toPromise(); + + this.deprecationsFactory = new DeprecationsFactory({ + logger: this.logger, + config: { + ignoredConfigDeprecations: config.skip_deprecated_settings, + }, + }); registerRoutes({ http }); this.registerConfigDeprecationsInfo(this.deprecationsFactory); + const deprecationsFactory = this.deprecationsFactory; return { getRegistry: (domainId: string): DeprecationsServiceSetup => { const registry = deprecationsFactory.getRegistry(domainId); @@ -156,6 +171,9 @@ export class DeprecationsService } public start(): InternalDeprecationsServiceStart { + if (!this.deprecationsFactory) { + throw new Error('`setup` must be called before `start`'); + } return { asScopedToClient: this.createScopedDeprecations(), }; @@ -169,7 +187,7 @@ export class DeprecationsService ) => DeprecationsClient { return (esClient: IScopedClusterClient, savedObjectsClient: SavedObjectsClientContract) => { return { - getAllDeprecations: this.deprecationsFactory.getAllDeprecations.bind(null, { + getAllDeprecations: this.deprecationsFactory!.getAllDeprecations.bind(null, { savedObjectsClient, esClient, }), @@ -178,7 +196,7 @@ export class DeprecationsService } private registerConfigDeprecationsInfo(deprecationsFactory: DeprecationsFactory) { - const handledDeprecatedConfigs = this.coreContext.configService.getHandledDeprecatedConfigs(); + const handledDeprecatedConfigs = this.configService.getHandledDeprecatedConfigs(); for (const [domainId, deprecationsContexts] of handledDeprecatedConfigs) { const deprecationsRegistry = deprecationsFactory.getRegistry(domainId); @@ -186,12 +204,14 @@ export class DeprecationsService getDeprecations: () => { return deprecationsContexts.map( ({ + configPath, title = `${domainId} has a deprecated setting`, level = 'critical', message, correctiveActions, documentationUrl, }) => ({ + configPath, title, level, message, diff --git a/src/core/server/deprecations/index.ts b/src/core/server/deprecations/index.ts index 5c2a0b87b42de..d9225750f04a1 100644 --- a/src/core/server/deprecations/index.ts +++ b/src/core/server/deprecations/index.ts @@ -7,7 +7,10 @@ */ export type { + BaseDeprecationDetails, DeprecationsDetails, + ConfigDeprecationDetails, + FeatureDeprecationDetails, GetDeprecationsContext, RegisterDeprecationsConfig, DeprecationsGetResponse, @@ -21,3 +24,4 @@ export type { } from './deprecations_service'; export { DeprecationsService } from './deprecations_service'; +export { config } from './deprecation_config'; diff --git a/src/core/server/deprecations/types.ts b/src/core/server/deprecations/types.ts index e24c6a13fceea..3990a76f7578e 100644 --- a/src/core/server/deprecations/types.ts +++ b/src/core/server/deprecations/types.ts @@ -6,22 +6,16 @@ * Side Public License, v 1. */ +import type { MaybePromise } from '@kbn/utility-types'; import type { SavedObjectsClientContract } from '../saved_objects/types'; import type { IScopedClusterClient } from '../elasticsearch'; -type MaybePromise = T | Promise; - -/** - * @internal - */ -export interface DomainDeprecationDetails extends DeprecationsDetails { - domainId: string; -} - /** + * Base properties shared by all types of deprecations + * * @public */ -export interface DeprecationsDetails { +export interface BaseDeprecationDetails { /** * The title of the deprecation. * Check the README for writing deprecations in `src/core/server/deprecations/README.mdx` @@ -82,6 +76,33 @@ export interface DeprecationsDetails { }; } +/** + * @public + */ +export interface ConfigDeprecationDetails extends BaseDeprecationDetails { + configPath: string; + deprecationType: 'config'; +} + +/** + * @public + */ +export interface FeatureDeprecationDetails extends BaseDeprecationDetails { + deprecationType?: 'feature' | undefined; +} + +/** + * @public + */ +export type DeprecationsDetails = ConfigDeprecationDetails | FeatureDeprecationDetails; + +/** + * @internal + */ +export type DomainDeprecationDetails = DeprecationsDetails & { + domainId: string; +}; + /** * @public */ diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts index f5d44a87d5404..71b9f87c14fa3 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.ts @@ -145,6 +145,7 @@ const deprecations: ConfigDeprecationProvider = () => [ } if (es.username === 'elastic') { addDeprecation({ + configPath: `${fromPath}.username`, message: `Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana_system" user instead.`, correctiveActions: { manualSteps: [`Replace [${fromPath}.username] from "elastic" to "kibana_system".`], @@ -152,6 +153,7 @@ const deprecations: ConfigDeprecationProvider = () => [ }); } else if (es.username === 'kibana') { addDeprecation({ + configPath: `${fromPath}.username`, message: `Setting [${fromPath}.username] to "kibana" is deprecated. You should use the "kibana_system" user instead.`, correctiveActions: { manualSteps: [`Replace [${fromPath}.username] from "kibana" to "kibana_system".`], @@ -160,6 +162,7 @@ const deprecations: ConfigDeprecationProvider = () => [ } if (es.ssl?.key !== undefined && es.ssl?.certificate === undefined) { addDeprecation({ + configPath: `${fromPath}.ssl.key`, message: `Setting [${fromPath}.ssl.key] without [${fromPath}.ssl.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`, correctiveActions: { manualSteps: [ @@ -169,6 +172,7 @@ const deprecations: ConfigDeprecationProvider = () => [ }); } else if (es.ssl?.certificate !== undefined && es.ssl?.key === undefined) { addDeprecation({ + configPath: `${fromPath}.ssl.certificate`, message: `Setting [${fromPath}.ssl.certificate] without [${fromPath}.ssl.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`, correctiveActions: { manualSteps: [ @@ -178,6 +182,7 @@ const deprecations: ConfigDeprecationProvider = () => [ }); } else if (es.logQueries === true) { addDeprecation({ + configPath: `${fromPath}.logQueries`, message: `Setting [${fromPath}.logQueries] is deprecated and no longer used. You should set the log level to "debug" for the "elasticsearch.queries" context in "logging.loggers" or use "logging.verbose: true".`, correctiveActions: { manualSteps: [ diff --git a/src/core/server/index.ts b/src/core/server/index.ts index a9f2f00707646..92c758f7e6980 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -387,7 +387,10 @@ export { EventLoopDelaysMonitor } from './metrics'; export type { I18nServiceSetup } from './i18n'; export type { + BaseDeprecationDetails, DeprecationsDetails, + ConfigDeprecationDetails, + FeatureDeprecationDetails, RegisterDeprecationsConfig, GetDeprecationsContext, DeprecationsServiceSetup, diff --git a/src/core/server/kibana_config.ts b/src/core/server/kibana_config.ts index ead3f2cc2776a..859f25d7082f1 100644 --- a/src/core/server/kibana_config.ts +++ b/src/core/server/kibana_config.ts @@ -17,6 +17,7 @@ const deprecations: ConfigDeprecationProvider = () => [ const kibana = settings[fromPath]; if (kibana?.index) { addDeprecation({ + configPath: 'kibana.index', title: i18n.translate('core.kibana.index.deprecationTitle', { defaultMessage: `Setting "kibana.index" is deprecated`, }), diff --git a/src/core/server/saved_objects/saved_objects_config.ts b/src/core/server/saved_objects/saved_objects_config.ts index 0c27f170f590b..c9b4b4499fa80 100644 --- a/src/core/server/saved_objects/saved_objects_config.ts +++ b/src/core/server/saved_objects/saved_objects_config.ts @@ -27,6 +27,7 @@ const migrationDeprecations: ConfigDeprecationProvider = () => [ const migrationsConfig = settings[fromPath]; if (migrationsConfig?.enableV2 !== undefined) { addDeprecation({ + configPath: `${fromPath}.enableV2`, message: '"migrations.enableV2" is deprecated and will be removed in an upcoming release without any further notice.', documentationUrl: 'https://ela.st/kbn-so-migration-v2', diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index 72ca2d15007b8..82a0dd71700f6 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -4011,16 +4011,7 @@ describe('SavedObjectsRepository', () => { ]; const originId = 'some-origin-id'; - const updateSuccess = async (type, id, attributes, options, includeOriginId) => { - if (registry.isMultiNamespace(type)) { - const mockGetResponse = getMockGetResponse({ type, id }, options?.namespace); - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - { ...mockGetResponse }, - { statusCode: 200 } - ) - ); - } + const mockUpdateResponse = (type, id, options, includeOriginId) => { client.update.mockResolvedValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise( { @@ -4042,6 +4033,19 @@ describe('SavedObjectsRepository', () => { { statusCode: 200 } ) ); + }; + + const updateSuccess = async (type, id, attributes, options, includeOriginId) => { + if (registry.isMultiNamespace(type)) { + const mockGetResponse = getMockGetResponse({ type, id }, options?.namespace); + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise( + { ...mockGetResponse }, + { statusCode: 200 } + ) + ); + } + mockUpdateResponse(type, id, options, includeOriginId); const result = await savedObjectsRepository.update(type, id, attributes, options); expect(client.get).toHaveBeenCalledTimes(registry.isMultiNamespace(type) ? 1 : 0); return result; @@ -4085,7 +4089,7 @@ describe('SavedObjectsRepository', () => { await test([]); }); - it(`uses the 'upsertAttributes' option when specified`, async () => { + it(`uses the 'upsertAttributes' option when specified for a single-namespace type`, async () => { await updateSuccess(type, id, attributes, { upsert: { title: 'foo', @@ -4109,6 +4113,42 @@ describe('SavedObjectsRepository', () => { ); }); + it(`uses the 'upsertAttributes' option when specified for a multi-namespace type that does not exist`, async () => { + const options = { upsert: { title: 'foo', description: 'bar' } }; + mockUpdateResponse(MULTI_NAMESPACE_ISOLATED_TYPE, id, options); + await savedObjectsRepository.update(MULTI_NAMESPACE_ISOLATED_TYPE, id, attributes, options); + expect(client.get).toHaveBeenCalledTimes(1); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:logstash-*`, + body: expect.objectContaining({ + upsert: expect.objectContaining({ + type: MULTI_NAMESPACE_ISOLATED_TYPE, + [MULTI_NAMESPACE_ISOLATED_TYPE]: { + title: 'foo', + description: 'bar', + }, + }), + }), + }), + expect.anything() + ); + }); + + it(`ignores use the 'upsertAttributes' option when specified for a multi-namespace type that already exists`, async () => { + const options = { upsert: { title: 'foo', description: 'bar' } }; + await updateSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, id, attributes, options); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:logstash-*`, + body: expect.not.objectContaining({ + upsert: expect.anything(), + }), + }), + expect.anything() + ); + }); + it(`doesn't accept custom references if not an array`, async () => { const test = async (references) => { await updateSuccess(type, id, attributes, { references }); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index 9cdc58f02f5d1..c74092faad96a 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -161,6 +161,35 @@ export interface SavedObjectsIncrementCounterField { incrementBy?: number; } +/** + * @internal + */ +interface PreflightCheckNamespacesParams { + /** The object type to fetch */ + type: string; + /** The object ID to fetch */ + id: string; + /** The current space */ + namespace: string | undefined; + /** Optional; for an object that is being created, this specifies the initial namespace(s) it will exist in (overriding the current space) */ + initialNamespaces?: string[]; +} + +/** + * @internal + */ +interface PreflightCheckNamespacesResult { + /** If the object exists, and whether or not it exists in the current space */ + checkResult: 'not_found' | 'found_in_namespace' | 'found_outside_namespace'; + /** + * What namespace(s) the object should exist in, if it needs to be created; practically speaking, this will never be undefined if + * checkResult == not_found or checkResult == found_in_namespace + */ + savedObjectNamespaces?: string[]; + /** The source of the raw document, if the object already exists */ + rawDocSource?: GetResponseFound; +} + /** * @public */ @@ -297,12 +326,16 @@ export class SavedObjectsRepository { if (id && overwrite) { // we will overwrite a multi-namespace saved object if it exists; if that happens, ensure we preserve its included namespaces // note: this check throws an error if the object is found but does not exist in this namespace - savedObjectNamespaces = await this.preflightGetNamespaces( + const preflightResult = await this.preflightCheckNamespaces({ type, id, namespace, - initialNamespaces - ); + initialNamespaces, + }); + if (preflightResult.checkResult === 'found_outside_namespace') { + throw SavedObjectsErrorHelpers.createConflictError(type, id); + } + savedObjectNamespaces = preflightResult.savedObjectNamespaces; } else { savedObjectNamespaces = initialNamespaces || getSavedObjectNamespaces(namespace); } @@ -670,11 +703,22 @@ export class SavedObjectsRepository { const namespace = normalizeNamespace(options.namespace); const rawId = this._serializer.generateRawId(namespace, type, id); - let preflightResult: SavedObjectsRawDoc | undefined; + let preflightResult: PreflightCheckNamespacesResult | undefined; if (this._registry.isMultiNamespace(type)) { - preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace); - const existingNamespaces = getSavedObjectNamespaces(undefined, preflightResult) ?? []; + // note: this check throws an error if the object is found but does not exist in this namespace + preflightResult = await this.preflightCheckNamespaces({ + type, + id, + namespace, + }); + if ( + preflightResult.checkResult === 'found_outside_namespace' || + preflightResult.checkResult === 'not_found' + ) { + throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); + } + const existingNamespaces = preflightResult.savedObjectNamespaces ?? []; if ( !force && (existingNamespaces.length > 1 || existingNamespaces.includes(ALL_NAMESPACES_STRING)) @@ -689,7 +733,7 @@ export class SavedObjectsRepository { { id: rawId, index: this.getIndexForType(type), - ...getExpectedVersionProperties(undefined, preflightResult), + ...getExpectedVersionProperties(undefined, preflightResult?.rawDocSource), refresh, }, { ignore: [404] } @@ -1208,22 +1252,33 @@ export class SavedObjectsRepository { const { version, references, upsert, refresh = DEFAULT_REFRESH_SETTING } = options; const namespace = normalizeNamespace(options.namespace); - let preflightResult: SavedObjectsRawDoc | undefined; + let preflightResult: PreflightCheckNamespacesResult | undefined; if (this._registry.isMultiNamespace(type)) { - preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace); + preflightResult = await this.preflightCheckNamespaces({ + type, + id, + namespace, + }); + if ( + preflightResult.checkResult === 'found_outside_namespace' || + (!upsert && preflightResult.checkResult === 'not_found') + ) { + throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); + } } const time = getCurrentTime(); let rawUpsert: SavedObjectsRawDoc | undefined; - if (upsert) { + // don't include upsert if the object already exists; ES doesn't allow upsert in combination with version properties + if (upsert && (!preflightResult || preflightResult.checkResult === 'not_found')) { let savedObjectNamespace: string | undefined; let savedObjectNamespaces: string[] | undefined; if (this._registry.isSingleNamespace(type) && namespace) { savedObjectNamespace = namespace; } else if (this._registry.isMultiNamespace(type)) { - savedObjectNamespaces = await this.preflightGetNamespaces(type, id, namespace); + savedObjectNamespaces = preflightResult!.savedObjectNamespaces; } const migrated = this._migrator.migrateDocument({ @@ -1249,9 +1304,8 @@ export class SavedObjectsRepository { .update({ id: this._serializer.generateRawId(namespace, type, id), index: this.getIndexForType(type), - ...getExpectedVersionProperties(version, preflightResult), + ...getExpectedVersionProperties(version, preflightResult?.rawDocSource), refresh, - body: { doc, ...(rawUpsert && { upsert: rawUpsert._source }), @@ -1753,7 +1807,16 @@ export class SavedObjectsRepository { if (this._registry.isSingleNamespace(type) && namespace) { savedObjectNamespace = namespace; } else if (this._registry.isMultiNamespace(type)) { - savedObjectNamespaces = await this.preflightGetNamespaces(type, id, namespace); + // note: this check throws an error if the object is found but does not exist in this namespace + const preflightResult = await this.preflightCheckNamespaces({ + type, + id, + namespace, + }); + if (preflightResult.checkResult === 'found_outside_namespace') { + throw SavedObjectsErrorHelpers.createConflictError(type, id); + } + savedObjectNamespaces = preflightResult.savedObjectNamespaces; } // attributes: { [counterFieldName]: incrementBy }, @@ -2047,24 +2110,14 @@ export class SavedObjectsRepository { } /** - * Pre-flight check to get a multi-namespace saved object's included namespaces. This ensures that, if the saved object exists, it - * includes the target namespace. - * - * @param type The type of the saved object. - * @param id The ID of the saved object. - * @param namespace The target namespace. - * @param initialNamespaces The target namespace(s) we intend to create the object in, if specified. - * @returns Array of namespaces that this saved object currently includes, or (if the object does not exist yet) the namespaces that a - * newly-created object will include. Value may be undefined if an existing saved object has no namespaces attribute; this should not - * happen in normal operations, but it is possible if the Elasticsearch document is manually modified. - * @throws Will throw an error if the saved object exists and it does not include the target namespace. + * Pre-flight check to ensure that a multi-namespace object exists in the current namespace. */ - private async preflightGetNamespaces( - type: string, - id: string, - namespace: string | undefined, - initialNamespaces?: string[] - ) { + private async preflightCheckNamespaces({ + type, + id, + namespace, + initialNamespaces, + }: PreflightCheckNamespacesParams): Promise { if (!this._registry.isMultiNamespace(type)) { throw new Error(`Cannot make preflight get request for non-multi-namespace type '${type}'.`); } @@ -2084,55 +2137,21 @@ export class SavedObjectsRepository { const indexFound = statusCode !== 404; if (indexFound && isFoundGetResponse(body)) { if (!this.rawDocExistsInNamespaces(body, namespaces)) { - throw SavedObjectsErrorHelpers.createConflictError(type, id); + return { checkResult: 'found_outside_namespace' }; } - return initialNamespaces ?? getSavedObjectNamespaces(namespace, body); + return { + checkResult: 'found_in_namespace', + savedObjectNamespaces: initialNamespaces ?? getSavedObjectNamespaces(namespace, body), + rawDocSource: body, + }; } else if (isNotFoundFromUnsupportedServer({ statusCode, headers })) { // checking if the 404 is from Elasticsearch throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(type, id); } - return initialNamespaces ?? getSavedObjectNamespaces(namespace); - } - - /** - * Pre-flight check for a multi-namespace saved object's namespaces. This ensures that, if the saved object exists, it includes the target - * namespace. - * - * @param type The type of the saved object. - * @param id The ID of the saved object. - * @param namespace The target namespace. - * @returns Raw document from Elasticsearch. - * @throws Will throw an error if the saved object is not found, if it doesn't include the target namespace or if the response is not identifiable as an Elasticsearch response. - */ - private async preflightCheckIncludesNamespace(type: string, id: string, namespace?: string) { - if (!this._registry.isMultiNamespace(type)) { - throw new Error(`Cannot make preflight get request for non-multi-namespace type '${type}'.`); - } - - const rawId = this._serializer.generateRawId(undefined, type, id); - const { body, statusCode, headers } = await this.client.get( - { - id: rawId, - index: this.getIndexForType(type), - }, - { ignore: [404] } - ); - - const indexFound = statusCode !== 404; - - // check if we have the elasticsearch header when index is not found and if we do, ensure it is Elasticsearch - if (isNotFoundFromUnsupportedServer({ statusCode, headers })) { - throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError(type, id); - } - - if ( - !indexFound || - !isFoundGetResponse(body) || - !this.rawDocExistsInNamespace(body, namespace) - ) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - return body; + return { + checkResult: 'not_found', + savedObjectNamespaces: initialNamespaces ?? getSavedObjectNamespaces(namespace), + }; } /** The `initialNamespaces` field (create, bulkCreate) is used to create an object in an initial set of spaces. */ diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a1e5d61744648..958083ae06899 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -33,6 +33,7 @@ import { LoggerFactory } from '@kbn/logging'; import { LogLevel } from '@kbn/logging'; import { LogMeta } from '@kbn/logging'; import { LogRecord } from '@kbn/logging'; +import { MaybePromise } from '@kbn/utility-types'; import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; @@ -156,6 +157,27 @@ export interface AuthToolkit { } & ResponseHeaders) => AuthResult; } +// @public +export interface BaseDeprecationDetails { + correctiveActions: { + api?: { + path: string; + method: 'POST' | 'PUT'; + body?: { + [key: string]: any; + }; + omitContextFromBody?: boolean; + }; + manualSteps: string[]; + }; + deprecationType?: 'config' | 'feature'; + documentationUrl?: string; + level: 'warning' | 'critical' | 'fetch_error'; + message: string; + requireRestart?: boolean; + title: string; +} + // @public export class BasePath { // @internal @@ -250,6 +272,14 @@ export { ConfigDeprecation } export { ConfigDeprecationContext } +// @public (undocumented) +export interface ConfigDeprecationDetails extends BaseDeprecationDetails { + // (undocumented) + configPath: string; + // (undocumented) + deprecationType: 'config'; +} + export { ConfigDeprecationFactory } export { ConfigDeprecationProvider } @@ -782,25 +812,7 @@ export interface DeprecationsClient { } // @public (undocumented) -export interface DeprecationsDetails { - correctiveActions: { - api?: { - path: string; - method: 'POST' | 'PUT'; - body?: { - [key: string]: any; - }; - omitContextFromBody?: boolean; - }; - manualSteps: string[]; - }; - deprecationType?: 'config' | 'feature'; - documentationUrl?: string; - level: 'warning' | 'critical' | 'fetch_error'; - message: string; - requireRestart?: boolean; - title: string; -} +export type DeprecationsDetails = ConfigDeprecationDetails | FeatureDeprecationDetails; // @public export interface DeprecationSettings { @@ -951,6 +963,12 @@ export interface FakeRequest { headers: Headers; } +// @public (undocumented) +export interface FeatureDeprecationDetails extends BaseDeprecationDetails { + // (undocumented) + deprecationType?: 'feature' | undefined; +} + // @public export type GetAuthHeaders = (request: KibanaRequest) => AuthHeaders | undefined; @@ -1676,8 +1694,6 @@ export type RedirectResponseOptions = HttpResponseOptions & { // @public (undocumented) export interface RegisterDeprecationsConfig { - // Warning: (ae-forgotten-export) The symbol "MaybePromise" needs to be exported by the entry point index.d.ts - // // (undocumented) getDeprecations: (context: GetDeprecationsContext) => MaybePromise; } diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 59cde6835c519..b6f263f0c22ff 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -51,7 +51,7 @@ import { ServiceConfigDescriptor, } from './internal_types'; import { CoreUsageDataService } from './core_usage_data'; -import { DeprecationsService } from './deprecations'; +import { DeprecationsService, config as deprecationConfig } from './deprecations'; import { CoreRouteHandlerContext } from './core_route_handler_context'; import { config as externalUrlConfig } from './external_url'; import { config as executionContextConfig } from './execution_context'; @@ -206,7 +206,7 @@ export class Server { executionContext: executionContextSetup, }); - const deprecationsSetup = this.deprecations.setup({ + const deprecationsSetup = await this.deprecations.setup({ http: httpSetup, }); @@ -388,6 +388,7 @@ export class Server { statusConfig, pidConfig, i18nConfig, + deprecationConfig, ]; this.configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider); diff --git a/src/plugins/custom_integrations/common/index.ts b/src/plugins/custom_integrations/common/index.ts index de2a6592465a2..944ac6ba3e6ee 100755 --- a/src/plugins/custom_integrations/common/index.ts +++ b/src/plugins/custom_integrations/common/index.ts @@ -40,8 +40,15 @@ export const INTEGRATION_CATEGORY_DISPLAY = { web: 'Web', // Kibana added - upload_file: 'Upload a file', + communication: 'Communication', + customer_support: 'Customer Support', + document_storage: 'Document Storage', + enterprise_management: 'Enterprise Management', + knowledge_platform: 'Knowledge Platform', language_client: 'Language client', + project_management: 'Project Management', + software_development: 'Software Development', + upload_file: 'Upload a file', }; /** @@ -70,6 +77,7 @@ export interface IntegrationCategoryCount { // TODO: consider i18n export const SHIPPER_DISPLAY = { beats: 'Beats', + enterprise_search: 'Enterprise Search', language_clients: 'Language clients', other: 'Other', sample_data: 'Sample data', diff --git a/src/plugins/data/common/search/expressions/kibana_context.test.ts b/src/plugins/data/common/search/expressions/kibana_context.test.ts index 77d89792b63c3..c9fe4ee5b7367 100644 --- a/src/plugins/data/common/search/expressions/kibana_context.test.ts +++ b/src/plugins/data/common/search/expressions/kibana_context.test.ts @@ -205,7 +205,7 @@ describe('kibanaContextFn', () => { it('deduplicates duplicated filters and keeps the first enabled filter', async () => { const { fn } = kibanaContextFn; const filter1 = buildFilter( - { fields: [] }, + { fields: [], title: 'dataView' }, { name: 'test', type: 'test' }, FILTERS.PHRASE, false, @@ -217,7 +217,7 @@ describe('kibanaContextFn', () => { FilterStateStore.APP_STATE ); const filter2 = buildFilter( - { fields: [] }, + { fields: [], title: 'dataView' }, { name: 'test', type: 'test' }, FILTERS.PHRASE, false, @@ -230,7 +230,7 @@ describe('kibanaContextFn', () => { ); const filter3 = buildFilter( - { fields: [] }, + { fields: [], title: 'dataView' }, { name: 'test', type: 'test' }, FILTERS.PHRASE, false, diff --git a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap index 3d4a9923fa8c8..82d9183f3d394 100644 --- a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap +++ b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap @@ -594,9 +594,13 @@ exports[`Source Viewer component renders json code editor 1`] = ` fallback={} > - +
+ +
diff --git a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts index 65089bc24317b..67309aab44a76 100644 --- a/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts +++ b/src/plugins/index_pattern_field_editor/__jest__/client_integration/field_editor_flyout_preview.test.ts @@ -366,6 +366,7 @@ describe('Field editor Preview panel', () => { subTitle: 'First doc - subTitle', title: 'First doc - title', }, + documentId: '001', index: 'testIndex', script: { source: 'echo("hello")', diff --git a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx index e49e0ef6885d0..21ab055c9b05e 100644 --- a/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/index_pattern_field_editor/public/components/preview/field_preview_context.tsx @@ -335,6 +335,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { document: params.document!, context: `${params.type!}_field` as FieldPreviewContext, script: params.script!, + documentId: currentDocId, }); if (currentApiCall !== previewCount.current) { diff --git a/src/plugins/index_pattern_field_editor/public/lib/api.ts b/src/plugins/index_pattern_field_editor/public/lib/api.ts index 9325b5c2faf47..9641619640a52 100644 --- a/src/plugins/index_pattern_field_editor/public/lib/api.ts +++ b/src/plugins/index_pattern_field_editor/public/lib/api.ts @@ -16,11 +16,13 @@ export const initApi = (httpClient: HttpSetup) => { context, script, document, + documentId, }: { index: string; context: FieldPreviewContext; script: { source: string } | null; document: Record; + documentId: string; }) => { return sendRequest(httpClient, { path: `${API_BASE_PATH}/field_preview`, @@ -30,6 +32,7 @@ export const initApi = (httpClient: HttpSetup) => { context, script, document, + documentId, }, }); }; diff --git a/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts b/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts index 11ec1ca7d5666..847dd41e0082b 100644 --- a/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts +++ b/src/plugins/index_pattern_field_editor/server/routes/field_preview.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import { estypes } from '@elastic/elasticsearch'; import { schema } from '@kbn/config-schema'; -import { HttpResponsePayload } from 'kibana/server'; import { API_BASE_PATH } from '../../common/constants'; import { RouteDependencies } from '../types'; @@ -26,6 +26,7 @@ const bodySchema = schema.object({ schema.literal('long_field'), ]), document: schema.object({}, { unknowns: 'allow' }), + documentId: schema.string(), }); export const registerFieldPreviewRoute = ({ router }: RouteDependencies): void => { @@ -39,30 +40,41 @@ export const registerFieldPreviewRoute = ({ router }: RouteDependencies): void = async (ctx, req, res) => { const { client } = ctx.core.elasticsearch; - const body = JSON.stringify({ - script: req.body.script, - context: req.body.context, - context_setup: { - document: req.body.document, - index: req.body.index, - } as any, - }); + const type = req.body.context.split('_field')[0] as estypes.MappingRuntimeFieldType; + const body = { + runtime_mappings: { + my_runtime_field: { + type, + script: req.body.script, + }, + }, + size: 1, + query: { + term: { + _id: req.body.documentId, + }, + }, + fields: ['my_runtime_field'], + }; try { - const response = await client.asCurrentUser.scriptsPainlessExecute({ - // @ts-expect-error `ExecutePainlessScriptRequest.body` does not allow `string` + const response = await client.asCurrentUser.search({ + index: req.body.index, body, }); - const fieldValue = response.body.result as any[] as HttpResponsePayload; + const fieldValue = response.body.hits.hits[0]?.fields?.my_runtime_field ?? ''; return res.ok({ body: { values: fieldValue } }); - } catch (error) { + } catch (error: any) { // Assume invalid painless script was submitted // Return 200 with error object const handleCustomError = () => { return res.ok({ - body: { values: [], ...error.body }, + body: { + values: [], + error: error.body.error.failed_shards[0]?.reason ?? {}, + }, }); }; diff --git a/src/plugins/kibana_legacy/server/index.ts b/src/plugins/kibana_legacy/server/index.ts index 90c9c2888c9da..15ae8547c73e1 100644 --- a/src/plugins/kibana_legacy/server/index.ts +++ b/src/plugins/kibana_legacy/server/index.ts @@ -27,6 +27,7 @@ export const config: PluginConfigDescriptor = { return; } addDeprecation({ + configPath: 'kibana.defaultAppId', message: `kibana.defaultAppId is deprecated and will be removed in 8.0. Please use the \`defaultRoute\` advanced setting instead`, correctiveActions: { manualSteps: [ diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx index 07bda19bf6d14..e0e2c4b3145c0 100644 --- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx +++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx @@ -14,6 +14,7 @@ import { monaco } from '@kbn/monaco'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import classNames from 'classnames'; +import './register_languages'; import { DARK_THEME, diff --git a/src/plugins/kibana_react/public/code_editor/code_editor_field.tsx b/src/plugins/kibana_react/public/code_editor/code_editor_field.tsx new file mode 100644 index 0000000000000..0e6ab21159f15 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/code_editor_field.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; +import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; +import { EuiFormControlLayout } from '@elastic/eui'; +import { CodeEditor, Props } from './code_editor'; + +/** + * Renders a Monaco code editor in the same style as other EUI form fields. + */ +export const CodeEditorField: React.FunctionComponent = (props) => { + const { width, height, options, fullWidth, useDarkTheme } = props; + const theme = useDarkTheme ? darkTheme : lightTheme; + const style = { + width, + height, + backgroundColor: options?.readOnly + ? theme.euiFormBackgroundReadOnlyColor + : theme.euiFormBackgroundColor, + }; + + return ( + + ); +}; diff --git a/src/plugins/kibana_react/public/code_editor/index.tsx b/src/plugins/kibana_react/public/code_editor/index.tsx index f7f9c3b7fc8da..9aab815e23544 100644 --- a/src/plugins/kibana_react/public/code_editor/index.tsx +++ b/src/plugins/kibana_react/public/code_editor/index.tsx @@ -7,28 +7,31 @@ */ import React from 'react'; -import { - EuiDelayRender, - EuiErrorBoundary, - EuiLoadingContent, - EuiFormControlLayout, -} from '@elastic/eui'; -import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; -import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; +import { EuiDelayRender, EuiErrorBoundary, EuiLoadingContent } from '@elastic/eui'; + import { useUiSetting } from '../ui_settings'; -import { Props } from './code_editor'; -import './register_languages'; +import type { Props } from './code_editor'; -export * from './languages'; +export * from './languages/constants'; const LazyBaseEditor = React.lazy(() => import('./code_editor')); - -const Fallback = () => ( - - - +const LazyCodeEditorField = React.lazy(() => + import('./code_editor_field').then((m) => ({ default: m.CodeEditorField })) ); +const Fallback: React.FunctionComponent<{ height: Props['height'] }> = ({ height }) => { + return ( + <> + {/* when height is known, set minHeight to avoid layout shift */} +
+ + + +
+ + ); +}; + export type CodeEditorProps = Props; /** @@ -40,7 +43,7 @@ export const CodeEditor: React.FunctionComponent = (props) => { const darkMode = useUiSetting('theme:darkMode'); return ( - }> + }> @@ -51,38 +54,11 @@ export const CodeEditor: React.FunctionComponent = (props) => { * Renders a Monaco code editor in the same style as other EUI form fields. */ export const CodeEditorField: React.FunctionComponent = (props) => { - const { width, height, options, fullWidth } = props; const darkMode = useUiSetting('theme:darkMode'); - const theme = darkMode ? darkTheme : lightTheme; - const style = { - width, - height, - backgroundColor: options?.readOnly - ? theme.euiFormBackgroundReadOnlyColor - : theme.euiFormBackgroundColor, - }; - return ( - ); diff --git a/src/plugins/kibana_react/public/code_editor/languages/constants.ts b/src/plugins/kibana_react/public/code_editor/languages/constants.ts new file mode 100644 index 0000000000000..af80e4ccc56e2 --- /dev/null +++ b/src/plugins/kibana_react/public/code_editor/languages/constants.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { LANG as CssLang } from './css/constants'; +export { LANG as MarkdownLang } from './markdown/constants'; +export { LANG as YamlLang } from './yaml/constants'; +export { LANG as HandlebarsLang } from './handlebars/constants'; diff --git a/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx b/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx index 0fed4d37e4f7f..c6250d73a8896 100644 --- a/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx +++ b/src/plugins/kibana_react/public/url_template_editor/url_template_editor.tsx @@ -66,7 +66,7 @@ export const UrlTemplateEditor: React.FC = ({ return; } - const { dispose } = monaco.languages.registerCompletionItemProvider(HandlebarsLang.ID, { + const { dispose } = monaco.languages.registerCompletionItemProvider(HandlebarsLang, { triggerCharacters: ['{', '/', '?', '&', '='], provideCompletionItems(model, position, context, token) { const { lineNumber } = position; @@ -124,7 +124,7 @@ export const UrlTemplateEditor: React.FC = ({ return (
{ expect(mockAddDeprecation.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { + "configPath": "telemetry.url", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"telemetry.url\\" from the Kibana configuration.", @@ -180,6 +181,7 @@ describe('deprecateEndpointConfigs', () => { expect(mockAddDeprecation.mock.calls[0]).toMatchInlineSnapshot(` Array [ Object { + "configPath": "telemetry.optInStatusUrl", "correctiveActions": Object { "manualSteps": Array [ "Remove \\"telemetry.optInStatusUrl\\" from the Kibana configuration.", diff --git a/src/plugins/telemetry/server/config/deprecations.ts b/src/plugins/telemetry/server/config/deprecations.ts index 013ee51a2ddd9..38553be7d5774 100644 --- a/src/plugins/telemetry/server/config/deprecations.ts +++ b/src/plugins/telemetry/server/config/deprecations.ts @@ -35,6 +35,7 @@ export const deprecateEndpointConfigs: ConfigDeprecation = ( } addDeprecation({ + configPath: fullConfigPath, title: i18n.translate('telemetry.endpointConfigs.deprecationTitle', { defaultMessage: 'Setting "{configPath}" is deprecated', values: { configPath: fullConfigPath }, diff --git a/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js b/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js index adee297fe0119..610c694e08121 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js +++ b/src/plugins/vis_types/timeseries/public/application/components/markdown_editor.js @@ -116,7 +116,7 @@ export class MarkdownEditor extends Component {
{ await es.indices.create({ index: INDEX_NAME, @@ -35,6 +38,15 @@ export default function ({ getService }: FtrProviderContext) { }); }; + const addDoc = async () => { + await es.index({ + index: INDEX_NAME, + id: DOC_ID, + body: document, + refresh: 'wait_for', + }); + }; + const deleteIndex = async () => { await es.indices.delete({ index: INDEX_NAME, @@ -42,12 +54,13 @@ export default function ({ getService }: FtrProviderContext) { }; describe('Field preview', function () { - before(async () => await createIndex()); + before(async () => { + await createIndex(); + await addDoc(); + }); after(async () => await deleteIndex()); describe('should return the script value', () => { - const document = { foo: 1, bar: 'hello' }; - const tests = [ { context: 'keyword_field', @@ -77,6 +90,7 @@ export default function ({ getService }: FtrProviderContext) { const payload = { script: test.script, document, + documentId: DOC_ID, context: test.context, index: INDEX_NAME, }; diff --git a/test/plugin_functional/plugins/core_plugin_deprecations/server/config.ts b/test/plugin_functional/plugins/core_plugin_deprecations/server/config.ts index 650567f761aa3..5b9145be52661 100644 --- a/test/plugin_functional/plugins/core_plugin_deprecations/server/config.ts +++ b/test/plugin_functional/plugins/core_plugin_deprecations/server/config.ts @@ -22,6 +22,7 @@ type ConfigType = TypeOf; const configSecretDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => { if (get(settings, 'corePluginDeprecations.secret') !== 42) { addDeprecation({ + configPath: 'corePluginDeprecations.secret', documentationUrl: 'config-secret-doc-url', message: 'Kibana plugin functional tests will no longer allow corePluginDeprecations.secret ' + diff --git a/test/plugin_functional/test_suites/core/deprecations.ts b/test/plugin_functional/test_suites/core/deprecations.ts index dbaf10008e35e..d8dc82a56cb55 100644 --- a/test/plugin_functional/test_suites/core/deprecations.ts +++ b/test/plugin_functional/test_suites/core/deprecations.ts @@ -19,6 +19,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide const CorePluginDeprecationsPluginDeprecations: DomainDeprecationDetails[] = [ { + configPath: 'corePluginDeprecations.oldProperty', title: 'Setting "corePluginDeprecations.oldProperty" is deprecated', level: 'critical', message: @@ -33,6 +34,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide requireRestart: true, }, { + configPath: 'corePluginDeprecations.noLongerUsed', title: 'Setting "corePluginDeprecations.noLongerUsed" is deprecated', level: 'critical', message: 'You no longer need to configure "corePluginDeprecations.noLongerUsed".', @@ -46,6 +48,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide requireRestart: true, }, { + configPath: 'corePluginDeprecations.secret', title: 'corePluginDeprecations has a deprecated setting', level: 'critical', message: diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index 26f7da073223d..b3e019a1bf46d 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -69,6 +69,7 @@ export const config: PluginConfigDescriptor = { ) ) { addDeprecation({ + configPath: 'xpack.actions.customHostSettings.ssl.rejectUnauthorized', message: `"xpack.actions.customHostSettings[].ssl.rejectUnauthorized" is deprecated.` + `Use "xpack.actions.customHostSettings[].ssl.verificationMode" instead, ` + @@ -96,6 +97,7 @@ export const config: PluginConfigDescriptor = { const actions = get(settings, fromPath); if (actions?.hasOwnProperty('rejectUnauthorized')) { addDeprecation({ + configPath: `${fromPath}.rejectUnauthorized`, message: `"xpack.actions.rejectUnauthorized" is deprecated. Use "xpack.actions.verificationMode" instead, ` + `with the setting "verificationMode:full" eql to "rejectUnauthorized:true", ` + @@ -122,6 +124,7 @@ export const config: PluginConfigDescriptor = { const actions = get(settings, fromPath); if (actions?.hasOwnProperty('proxyRejectUnauthorizedCertificates')) { addDeprecation({ + configPath: `${fromPath}.proxyRejectUnauthorizedCertificates`, message: `"xpack.actions.proxyRejectUnauthorizedCertificates" is deprecated. Use "xpack.actions.proxyVerificationMode" instead, ` + `with the setting "proxyVerificationMode:full" eql to "rejectUnauthorized:true",` + @@ -148,6 +151,7 @@ export const config: PluginConfigDescriptor = { const actions = get(settings, fromPath); if (actions?.enabled === false || actions?.enabled === true) { addDeprecation({ + configPath: 'xpack.actions.enabled', message: `"xpack.actions.enabled" is deprecated. The ability to disable this plugin will be removed in 8.0.0.`, correctiveActions: { manualSteps: [`Remove "xpack.actions.enabled" from your kibana configs.`], diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index 3b4688173e9b5..b7a510a37661b 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -62,6 +62,7 @@ export const config: PluginConfigDescriptor = { const alerting = get(settings, fromPath); if (alerting?.enabled === false || alerting?.enabled === true) { addDeprecation({ + configPath: 'xpack.alerting.enabled', message: `"xpack.alerting.enabled" is deprecated. The ability to disable this plugin will be removed in 8.0.0.`, correctiveActions: { manualSteps: [`Remove "xpack.alerting.enabled" from your kibana configs.`], diff --git a/x-pack/plugins/apm/common/anomaly_detection.ts b/x-pack/plugins/apm/common/anomaly_detection.ts index eb7c74a4540be..43a779407d2a4 100644 --- a/x-pack/plugins/apm/common/anomaly_detection.ts +++ b/x-pack/plugins/apm/common/anomaly_detection.ts @@ -33,8 +33,6 @@ export function getSeverityColor(score: number) { return mlGetSeverityColor(score); } -export const ML_TRANSACTION_LATENCY_DETECTOR_INDEX = 0; - export const ML_ERRORS = { INVALID_LICENSE: i18n.translate( 'xpack.apm.anomaly_detection.error.invalid_license', diff --git a/x-pack/plugins/apm/common/utils/apm_ml_anomaly_query.ts b/x-pack/plugins/apm/common/utils/apm_ml_anomaly_query.ts deleted file mode 100644 index 26b859d37cf7f..0000000000000 --- a/x-pack/plugins/apm/common/utils/apm_ml_anomaly_query.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. - */ - -export function apmMlAnomalyQuery(detectorIndex: 0 | 1 | 2) { - return [ - { - bool: { - filter: [ - { - terms: { - result_type: ['model_plot', 'record'], - }, - }, - { - term: { detector_index: detectorIndex }, - }, - ], - }, - }, - ]; -} diff --git a/x-pack/plugins/apm/dev_docs/routing_and_linking.md b/x-pack/plugins/apm/dev_docs/routing_and_linking.md index 478de0081fca4..af22bcdbdfa11 100644 --- a/x-pack/plugins/apm/dev_docs/routing_and_linking.md +++ b/x-pack/plugins/apm/dev_docs/routing_and_linking.md @@ -6,15 +6,15 @@ This document describes routing in the APM plugin. ### Server-side -Route definitions for APM's server-side API are in the [server/routes directory](../server/routes). Routes are created with [the `createRoute` function](../server/routes/create_route.ts). Routes are added to the API in [the `createApmApi` function](../server/routes/create_apm_api.ts), which is initialized in the plugin `start` lifecycle method. +Route definitions for APM's server-side API are in the [server/routes directory](../server/routes). Routes are created with [the `createApmServerRoute` function](../server/routes/create_apm_server_route.ts). Routes are added to the API in [the `registerRoutes` function](../server/routes/register_routes.ts), which is initialized in the plugin `setup` lifecycle method. -The path and query string parameters are defined in the calls to `createRoute` with io-ts types, so that each route has its parameters type checked. +The path and query string parameters are defined in the calls to `createApmServerRoute` with io-ts types, so that each route has its parameters type checked. ### Client-side The client-side routing uses `@kbn/typed-react-router-config`, which is a wrapper around [React Router](https://reactrouter.com/) and [React Router Config](https://www.npmjs.com/package/react-router-config). Its goal is to provide a layer of high-fidelity types that allows us to parse and format URLs for routes while making sure the needed parameters are provided and/or available (typed and validated at runtime). The `history` object used by React Router is injected by the Kibana Platform. -Routes (and their parameters) are defined in [public/components/routing/apm_config.tsx](../public/components/routing/apm_config.tsx). +Routes (and their parameters) are defined in [public/components/routing/apm_route_config.tsx](../public/components/routing/apm_route_config.tsx). #### Parameter handling diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/test-tsconfig.json b/x-pack/plugins/apm/scripts/optimize-tsconfig/test-tsconfig.json index d6718b7511179..7f7cbf4e3efa2 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/test-tsconfig.json +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/test-tsconfig.json @@ -3,7 +3,8 @@ "types": [ "node" ], - "noErrorTruncation": true + "noErrorTruncation": true, + "assumeChangesOnlyAffectDirectDependencies": true }, "include": [ "./apm_api_integration/**/*", @@ -11,6 +12,8 @@ "../../typings/**/*" ], "exclude": [ - "**/__fixtures__/**/*" + "**/__fixtures__/**/*", + "**/target/**", + "**/node_modules/**" ] } diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json index 8254ec67eb3c5..2ece69d491fca 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json @@ -13,6 +13,7 @@ "**/node_modules/**" ], "compilerOptions": { - "noErrorTruncation": true + "noErrorTruncation": true, + "assumeChangesOnlyAffectDirectDependencies": true } } diff --git a/x-pack/plugins/apm/scripts/precommit.js b/x-pack/plugins/apm/scripts/precommit.js index 89c5055c6a7f7..a259cf932c912 100644 --- a/x-pack/plugins/apm/scripts/precommit.js +++ b/x-pack/plugins/apm/scripts/precommit.js @@ -66,7 +66,9 @@ const tasks = new Listr( execa( 'node', [ - resolve(__dirname, './jest.js'), + resolve(__dirname, '../../../../scripts/jest.js'), + '--config', + resolve(__dirname, '../jest.config.js'), '--reporters', resolve(__dirname, '../../../../node_modules/jest-silent-reporter'), '--collect-coverage', diff --git a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts index 10758b6d90cdc..4d4bc8dc185ab 100644 --- a/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts +++ b/x-pack/plugins/apm/server/lib/anomaly_detection/create_anomaly_detection_jobs.ts @@ -5,21 +5,21 @@ * 2.0. */ -import Boom from '@hapi/boom'; import { Logger } from 'kibana/server'; +import uuid from 'uuid/v4'; import { snakeCase } from 'lodash'; +import Boom from '@hapi/boom'; import moment from 'moment'; -import uuid from 'uuid/v4'; import { ML_ERRORS } from '../../../common/anomaly_detection'; -import { - METRICSET_NAME, - PROCESSOR_EVENT, -} from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; import { environmentQuery } from '../../../common/utils/environment_query'; -import { withApmSpan } from '../../utils/with_apm_span'; import { Setup } from '../helpers/setup_request'; +import { + TRANSACTION_DURATION, + PROCESSOR_EVENT, +} from '../../../common/elasticsearch_fieldnames'; import { APM_ML_JOB_GROUP, ML_MODULE_ID_APM_TRANSACTION } from './constants'; +import { withApmSpan } from '../../utils/with_apm_span'; import { getAnomalyDetectionJobs } from './get_anomaly_detection_jobs'; export async function createAnomalyDetectionJobs( @@ -92,8 +92,8 @@ async function createAnomalyDetectionJob({ query: { bool: { filter: [ - { term: { [PROCESSOR_EVENT]: ProcessorEvent.metric } }, - { term: { [METRICSET_NAME]: 'transaction' } }, + { term: { [PROCESSOR_EVENT]: ProcessorEvent.transaction } }, + { exists: { field: TRANSACTION_DURATION } }, ...environmentQuery(environment), ], }, @@ -105,7 +105,7 @@ async function createAnomalyDetectionJob({ job_tags: { environment, // identifies this as an APM ML job & facilitates future migrations - apm_ml_version: 3, + apm_ml_version: 2, }, }, }, diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts index 97c95e4e40045..9b2d79dc726ee 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_anomalies.ts @@ -11,11 +11,7 @@ import { estypes } from '@elastic/elasticsearch'; import { ESSearchResponse } from '../../../../../../src/core/types/elasticsearch'; import { MlPluginSetup } from '../../../../ml/server'; import { PromiseReturnType } from '../../../../observability/typings/common'; -import { - getSeverity, - ML_ERRORS, - ML_TRANSACTION_LATENCY_DETECTOR_INDEX, -} from '../../../common/anomaly_detection'; +import { getSeverity, ML_ERRORS } from '../../../common/anomaly_detection'; import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; import { getServiceHealthStatus } from '../../../common/service_health_status'; import { @@ -26,7 +22,6 @@ import { rangeQuery } from '../../../../observability/server'; import { withApmSpan } from '../../utils/with_apm_span'; import { getMlJobsWithAPMGroup } from '../anomaly_detection/get_ml_jobs_with_apm_group'; import { Setup } from '../helpers/setup_request'; -import { apmMlAnomalyQuery } from '../../../common/utils/apm_ml_anomaly_query'; export const DEFAULT_ANOMALIES: ServiceAnomaliesResponse = { mlJobIds: [], @@ -61,7 +56,7 @@ export async function getServiceAnomalies({ query: { bool: { filter: [ - ...apmMlAnomalyQuery(ML_TRANSACTION_LATENCY_DETECTOR_INDEX), + { terms: { result_type: ['model_plot', 'record'] } }, ...rangeQuery( Math.min(end - 30 * 60 * 1000, start), end, diff --git a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts index a7357bbc1dd34..a61e0614f5b1a 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_anomaly_data/fetcher.ts @@ -12,8 +12,6 @@ import { rangeQuery } from '../../../../../observability/server'; import { asMutableArray } from '../../../../common/utils/as_mutable_array'; import { withApmSpan } from '../../../utils/with_apm_span'; import { Setup } from '../../helpers/setup_request'; -import { apmMlAnomalyQuery } from '../../../../common/utils/apm_ml_anomaly_query'; -import { ML_TRANSACTION_LATENCY_DETECTOR_INDEX } from '../../../../common/anomaly_detection'; export type ESResponse = Exclude< PromiseReturnType, @@ -42,7 +40,7 @@ export function anomalySeriesFetcher({ query: { bool: { filter: [ - ...apmMlAnomalyQuery(ML_TRANSACTION_LATENCY_DETECTOR_INDEX), + { terms: { result_type: ['model_plot', 'record'] } }, { term: { partition_field_value: serviceName } }, { term: { by_field_value: transactionType } }, ...rangeQuery(start, end, 'timestamp'), diff --git a/x-pack/plugins/banners/server/config.ts b/x-pack/plugins/banners/server/config.ts index 37b4c57fc2ce1..18c6dc7c04687 100644 --- a/x-pack/plugins/banners/server/config.ts +++ b/x-pack/plugins/banners/server/config.ts @@ -44,6 +44,8 @@ export const config: PluginConfigDescriptor = { const pluginConfig = get(rootConfig, fromPath); if (pluginConfig?.placement === 'header') { addDeprecation({ + configPath: 'xpack.banners.placement', + level: 'critical', message: 'The `header` value for xpack.banners.placement has been replaced by `top`', correctiveActions: { manualSteps: [ diff --git a/x-pack/plugins/enterprise_search/kibana.json b/x-pack/plugins/enterprise_search/kibana.json index 5f33fa27c6640..7a844acfa9740 100644 --- a/x-pack/plugins/enterprise_search/kibana.json +++ b/x-pack/plugins/enterprise_search/kibana.json @@ -4,7 +4,7 @@ "kibanaVersion": "kibana", "requiredPlugins": ["features", "spaces", "licensing", "data", "charts", "infra"], "configPath": ["enterpriseSearch"], - "optionalPlugins": ["usageCollection", "security", "home", "cloud"], + "optionalPlugins": ["usageCollection", "security", "home", "cloud", "customIntegrations"], "server": true, "ui": true, "requiredBundles": ["home", "kibanaReact"], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx index 8e1e6487197f9..7539055253732 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx @@ -132,7 +132,7 @@ export const CurationSuggestion: React.FC = () => { result={result} isMetaEngine={isMetaEngine} schemaForTypeHighlights={engine.schema} - resultPosition={index + 1} + resultPosition={index + existingCurationResults.length + 1} /> ))} @@ -152,7 +152,7 @@ export const CurationSuggestion: React.FC = () => { result={result} isMetaEngine={isMetaEngine} schemaForTypeHighlights={engine.schema} - resultPosition={index + 1} + resultPosition={index + suggestedPromotedDocuments.length + 1} /> ))} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/components/log_retention_callout.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/components/log_retention_callout.tsx index 1fd1a9a79b225..801ed296137c7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/components/log_retention_callout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/components/log_retention_callout.tsx @@ -19,12 +19,14 @@ import { AppLogic } from '../../../app_logic'; import { SETTINGS_PATH } from '../../../routes'; import { ANALYTICS_TITLE } from '../../analytics'; import { API_LOGS_TITLE } from '../../api_logs'; +import { CRAWLER_TITLE } from '../../crawler'; import { LogRetentionLogic, LogRetentionOptions, renderLogRetentionDate } from '../index'; const TITLE_MAP = { [LogRetentionOptions.Analytics]: ANALYTICS_TITLE, [LogRetentionOptions.API]: API_LOGS_TITLE, + [LogRetentionOptions.Crawler]: CRAWLER_TITLE, }; interface Props { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts index 251fd93069afe..20c5860160b78 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/log_retention_logic.test.ts @@ -33,6 +33,11 @@ describe('LogRetentionLogic', () => { enabled: true, retention_policy: { is_default: true, min_age_days: 180 }, }, + crawler: { + disabled_at: null, + enabled: true, + retention_policy: { is_default: true, min_age_days: 180 }, + }, }; const TYPICAL_CLIENT_LOG_RETENTION = { @@ -46,6 +51,11 @@ describe('LogRetentionLogic', () => { enabled: true, retentionPolicy: { isDefault: true, minAgeDays: 180 }, }, + crawler: { + disabledAt: null, + enabled: true, + retentionPolicy: { isDefault: true, minAgeDays: 180 }, + }, }; const DEFAULT_VALUES = { @@ -146,6 +156,11 @@ describe('LogRetentionLogic', () => { enabled: true, retentionPolicy: null, }, + crawler: { + disabledAt: null, + enabled: true, + retentionPolicy: null, + }, }); expect(LogRetentionLogic.values).toEqual({ @@ -161,6 +176,11 @@ describe('LogRetentionLogic', () => { enabled: true, retentionPolicy: null, }, + crawler: { + disabledAt: null, + enabled: true, + retentionPolicy: null, + }, }, }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/messaging/constants.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/messaging/constants.tsx index c7c4d90d91ce8..e1e6820204d94 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/messaging/constants.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/messaging/constants.tsx @@ -37,6 +37,16 @@ const CAPITALIZATION_MAP = { { defaultMessage: 'API' } ), }, + [LogRetentionOptions.Crawler]: { + capitalized: i18n.translate( + 'xpack.enterpriseSearch.appSearch.logRetention.type.crawler.title.capitalized', + { defaultMessage: 'Web crawler' } + ), + lowercase: i18n.translate( + 'xpack.enterpriseSearch.appSearch.logRetention.type.crawler.title.lowercase', + { defaultMessage: 'web crawler' } + ), + }, }; interface Props { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/types.ts index a03154b8c57c2..a2c615aee3a0e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/types.ts @@ -8,11 +8,13 @@ export enum LogRetentionOptions { Analytics = 'analytics', API = 'api', + Crawler = 'crawler', } export interface LogRetention { [LogRetentionOptions.Analytics]: LogRetentionSettings; [LogRetentionOptions.API]: LogRetentionSettings; + [LogRetentionOptions.Crawler]: LogRetentionSettings; } export interface LogRetentionPolicy { @@ -29,6 +31,7 @@ export interface LogRetentionSettings { export interface LogRetentionServer { [LogRetentionOptions.Analytics]: LogRetentionServerSettings; [LogRetentionOptions.API]: LogRetentionServerSettings; + [LogRetentionOptions.Crawler]: LogRetentionServerSettings; } export interface LogRetentionServerPolicy { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.test.ts index b67be6d435c2e..b6a5997209247 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.test.ts @@ -21,6 +21,11 @@ describe('convertLogRetentionFromServerToClient', () => { enabled: true, retention_policy: { is_default: true, min_age_days: 180 }, }, + crawler: { + disabled_at: null, + enabled: true, + retention_policy: { is_default: true, min_age_days: 180 }, + }, }) ).toEqual({ analytics: { @@ -33,6 +38,11 @@ describe('convertLogRetentionFromServerToClient', () => { enabled: true, retentionPolicy: { isDefault: true, minAgeDays: 180 }, }, + crawler: { + disabledAt: null, + enabled: true, + retentionPolicy: { isDefault: true, minAgeDays: 180 }, + }, }); }); @@ -49,6 +59,11 @@ describe('convertLogRetentionFromServerToClient', () => { enabled: true, retention_policy: { is_default: true, min_age_days: null }, }, + crawler: { + disabled_at: null, + enabled: true, + retention_policy: { is_default: true, min_age_days: null }, + }, }) ).toEqual({ analytics: { @@ -61,6 +76,11 @@ describe('convertLogRetentionFromServerToClient', () => { enabled: true, retentionPolicy: { isDefault: true, minAgeDays: null }, }, + crawler: { + disabledAt: null, + enabled: true, + retentionPolicy: { isDefault: true, minAgeDays: null }, + }, }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.ts index cc8b84b667b68..fd2452d05f530 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/log_retention/utils/convert_log_retention.ts @@ -24,6 +24,9 @@ export const convertLogRetentionFromServerToClient = ( [LogRetentionOptions.API]: convertLogRetentionSettingsFromServerToClient( logRetention[LogRetentionOptions.API] ), + [LogRetentionOptions.Crawler]: convertLogRetentionSettingsFromServerToClient( + logRetention[LogRetentionOptions.Crawler] + ), }); const convertLogRetentionSettingsFromServerToClient = ({ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.test.tsx index fc20a9dbc5519..d04b6613dc671 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.test.tsx @@ -39,6 +39,13 @@ describe('', () => { minAgeDays: 7, }, }, + crawler: { + enabled: true, + retentionPolicy: { + isDefault: true, + minAgeDays: 7, + }, + }, }, }; @@ -128,4 +135,42 @@ describe('', () => { expect(actions.closeModals).toHaveBeenCalled(); }); }); + + describe('crawler', () => { + it('renders the Crawler panel when openedModal is set to Crawler', () => { + setMockValues({ + ...values, + openedModal: LogRetentionOptions.Crawler, + }); + + const logRetentionPanel = shallow(); + expect( + logRetentionPanel.find('[data-test-subj="CrawlerLogRetentionConfirmationModal"]').length + ).toBe(1); + }); + + it('calls saveLogRetention on save when showing crawler', () => { + setMockValues({ + ...values, + openedModal: LogRetentionOptions.Crawler, + }); + + const logRetentionPanel = shallow(); + const genericConfirmationModal = logRetentionPanel.find(GenericConfirmationModal); + genericConfirmationModal.prop('onSave')(); + expect(actions.saveLogRetention).toHaveBeenCalledWith(LogRetentionOptions.Crawler, false); + }); + + it('calls closeModals on close', () => { + setMockValues({ + ...values, + openedModal: LogRetentionOptions.Crawler, + }); + + const logRetentionPanel = shallow(); + const genericConfirmationModal = logRetentionPanel.find(GenericConfirmationModal); + genericConfirmationModal.prop('onClose')(); + expect(actions.closeModals).toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.tsx index 28d4257f2487c..694d41f11bbff 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_confirmation_modal.tsx @@ -133,6 +133,54 @@ export const LogRetentionConfirmationModal: React.FC = () => { onSave={() => saveLogRetention(LogRetentionOptions.API, false)} /> )} + + {openedModal === LogRetentionOptions.Crawler && ( + +

+ {i18n.translate( + 'xpack.enterpriseSearch.appSearch.settings.logRetention.modal.crawler.description', + { + defaultMessage: + 'When you disable writing, engines stop logging Web Crawler events. Your existing data is deleted according to the storage time frame.', + } + )} +

+

+ + {CANNOT_BE_RECOVERED_TEXT} + +

+ + } + target={DISABLE_TEXT} + onClose={closeModals} + onSave={() => saveLogRetention(LogRetentionOptions.Crawler, false)} + /> + )} ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.test.tsx index 3f6bbe4307b57..0d096727d481c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.test.tsx @@ -101,7 +101,7 @@ describe('', () => { ).toEqual(true); }); - it('enables both switches when isLogRetentionUpdating is false', () => { + it('enables all switches when isLogRetentionUpdating is false', () => { setMockValues({ isLogRetentionUpdating: false, logRetention: mockLogRetention({}), @@ -113,9 +113,12 @@ describe('', () => { expect( logRetentionPanel.find('[data-test-subj="LogRetentionPanelAPISwitch"]').prop('disabled') ).toEqual(false); + expect( + logRetentionPanel.find('[data-test-subj="LogRetentionPanelCrawlerSwitch"]').prop('disabled') + ).toEqual(false); }); - it('disables both switches when isLogRetentionUpdating is true', () => { + it('disables all switches when isLogRetentionUpdating is true', () => { setMockValues({ isLogRetentionUpdating: true, logRetention: mockLogRetention({}), @@ -128,6 +131,9 @@ describe('', () => { expect( logRetentionPanel.find('[data-test-subj="LogRetentionPanelAPISwitch"]').prop('disabled') ).toEqual(true); + expect( + logRetentionPanel.find('[data-test-subj="LogRetentionPanelCrawlerSwitch"]').prop('disabled') + ).toEqual(true); }); it('calls toggleLogRetention when analytics log retention option is changed', () => { @@ -150,7 +156,7 @@ describe('', () => { setMockValues({ isLogRetentionUpdating: false, logRetention: mockLogRetention({ - analytics: { + api: { enabled: false, }, }), @@ -159,6 +165,20 @@ describe('', () => { logRetentionPanel.find('[data-test-subj="LogRetentionPanelAPISwitch"]').simulate('change'); expect(actions.toggleLogRetention).toHaveBeenCalledWith('api'); }); + + it('calls toggleLogRetention when crawler log retention option is changed', () => { + setMockValues({ + isLogRetentionUpdating: false, + logRetention: mockLogRetention({ + crawler: { + enabled: false, + }, + }), + }); + const logRetentionPanel = shallow(); + logRetentionPanel.find('[data-test-subj="LogRetentionPanelCrawlerSwitch"]').simulate('change'); + expect(actions.toggleLogRetention).toHaveBeenCalledWith('crawler'); + }); }); const mockLogRetention = (logRetention: Partial) => { @@ -173,6 +193,11 @@ const mockLogRetention = (logRetention: Partial) => { enabled: true, retentionPolicy: { isDefault: true, minAgeDays: 180 }, }, + crawler: { + disabledAt: null, + enabled: true, + retentionPolicy: { isDefault: true, minAgeDays: 180 }, + }, }; return { @@ -184,5 +209,9 @@ const mockLogRetention = (logRetention: Partial) => { ...baseLogRetention.api, ...logRetention.api, }, + crawler: { + ...baseLogRetention.crawler, + ...logRetention.crawler, + }, }; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx index 9012a30b950d6..22a3ea6473183 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/settings/log_retention/log_retention_panel.tsx @@ -32,6 +32,7 @@ export const LogRetentionPanel: React.FC = () => { const hasILM = logRetention !== null; const analyticsLogRetentionSettings = logRetention?.[LogRetentionOptions.Analytics]; const apiLogRetentionSettings = logRetention?.[LogRetentionOptions.API]; + const crawlerLogRetentionSettings = logRetention?.[LogRetentionOptions.Crawler]; useEffect(() => { fetchLogRetention(); @@ -100,6 +101,33 @@ export const LogRetentionPanel: React.FC = () => { data-test-subj="LogRetentionPanelAPISwitch" /> + + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.settings.logRetention.crawler.label', + { + defaultMessage: 'Web Crawler Logs', + } + )} + + {': '} + {hasILM && ( + + + + )} + + } + checked={!!crawlerLogRetentionSettings?.enabled} + onChange={() => toggleLogRetention(LogRetentionOptions.Crawler)} + disabled={isLogRetentionUpdating} + data-test-subj="LogRetentionPanelCrawlerSwitch" + /> +

diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx index a682e10269e6c..db4f80dc37f4b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency.tsx @@ -26,10 +26,10 @@ import { ViewContentHeader } from '../../../../components/shared/view_content_he import { NAV, RESET_BUTTON } from '../../../../constants'; import { DIFFERENT_SYNC_TYPES_DOCS_URL } from '../../../../routes'; import { + LEARN_MORE_LINK, SOURCE_FREQUENCY_DESCRIPTION, SOURCE_SYNC_FREQUENCY_TITLE, BLOCKED_TIME_WINDOWS_TITLE, - SYNC_FREQUENCY_LINK_LABEL, SYNC_UNSAVED_CHANGES_MESSAGE, } from '../../constants'; import { SourceLogic } from '../../source_logic'; @@ -84,16 +84,6 @@ export const Frequency: React.FC = ({ tabId }) => { ); - const docsLinks = ( - - - - {SYNC_FREQUENCY_LINK_LABEL} - - - - ); - return ( = ({ tabId }) => { /> + {SOURCE_FREQUENCY_DESCRIPTION}{' '} + + {LEARN_MORE_LINK} + + + } action={actions} /> - {docsLinks} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx index 54ce0563ddeb3..2dfa2a6420f7f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/objects_and_assets.tsx @@ -27,11 +27,11 @@ import { ViewContentHeader } from '../../../../components/shared/view_content_he import { NAV, RESET_BUTTON } from '../../../../constants'; import { OBJECTS_AND_ASSETS_DOCS_URL } from '../../../../routes'; import { + LEARN_MORE_LINK, SYNC_MANAGEMENT_CONTENT_EXTRACTION_LABEL, SYNC_MANAGEMENT_THUMBNAILS_LABEL, SYNC_MANAGEMENT_THUMBNAILS_GLOBAL_CONFIG_LABEL, SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION, - OBJECTS_AND_ASSETS_LINK_LABEL, SOURCE_OBJECTS_AND_ASSETS_LABEL, SYNC_UNSAVED_CHANGES_MESSAGE, } from '../../constants'; @@ -84,12 +84,16 @@ export const ObjectsAndAssets: React.FC = () => { /> + {SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION}{' '} + + {LEARN_MORE_LINK} + + + } action={actions} /> - - {OBJECTS_AND_ASSETS_LINK_LABEL} - {SOURCE_OBJECTS_AND_ASSETS_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx index fb9cdc6916fa9..2d1e8105c12b2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.test.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiLink, EuiCallOut, EuiSwitch } from '@elastic/eui'; +import { EuiCallOut, EuiSwitch } from '@elastic/eui'; import { Synchronization } from './synchronization'; @@ -28,7 +28,6 @@ describe('Synchronization', () => { it('renders when config enabled', () => { const wrapper = shallow(); - expect(wrapper.find(EuiLink)).toHaveLength(1); expect(wrapper.find(EuiSwitch)).toHaveLength(1); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx index e88d4d251fa54..dec275adb3c50 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/synchronization.tsx @@ -15,12 +15,12 @@ import { ViewContentHeader } from '../../../../components/shared/view_content_he import { NAV } from '../../../../constants'; import { SYNCHRONIZATION_DOCS_URL } from '../../../../routes'; import { + LEARN_MORE_LINK, SOURCE_SYNCHRONIZATION_DESCRIPTION, SYNCHRONIZATION_DISABLED_TITLE, SYNCHRONIZATION_DISABLED_DESCRIPTION, SOURCE_SYNCHRONIZATION_TOGGLE_LABEL, SOURCE_SYNCHRONIZATION_TOGGLE_DESCRIPTION, - SYNCHRONIZATION_LINK_LABEL, } from '../../constants'; import { SourceLogic } from '../../source_logic'; import { SourceLayout } from '../source_layout'; @@ -65,11 +65,15 @@ export const Synchronization: React.FC = () => { > + {SOURCE_SYNCHRONIZATION_DESCRIPTION}{' '} + + {LEARN_MORE_LINK} + + + } /> - - {SYNCHRONIZATION_LINK_LABEL} - {isSyncConfigEnabled ? syncToggle : syncDisabledCallout} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts index f44dbae0608ea..087716e565ad0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/constants.ts @@ -531,7 +531,7 @@ export const SOURCE_SYNCHRONIZATION_DESCRIPTION = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.sources.sourceSynchronizationDescription', { defaultMessage: - 'Synchronization provides control over data being indexed from the content source. Enable synchronization of data from the content source to Workplace Search.', + 'Enable or disable synchronization of data from this content source to Workplace Search.', } ); @@ -539,7 +539,7 @@ export const SOURCE_FREQUENCY_DESCRIPTION = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.sources.sourceFrequencyDescription', { defaultMessage: - 'Schedule the frequency of data synchronization between Workplace search and the content source. Indexing schedules that occur less frequently lower the burden on third-party servers, while more frequent will ensure your data is up-to-date.', + 'Manage the frequency of data synchronization from Workplace search to this content source. Sync more frequently to ensure your data is up to date. Sync less frequently to reduce the burden on third party servers.', } ); @@ -547,7 +547,7 @@ export const SOURCE_OBJECTS_AND_ASSETS_DESCRIPTION = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.sources.sourceObjectsAndAssetsDescription', { defaultMessage: - 'Customize the indexing rules that determine what data is synchronized from this content source to Workplace Search.', + 'Customize the indexing rules that determine which objects and assets are synchronized from this content source to Workplace Search.', } ); @@ -636,13 +636,6 @@ export const BLOCKED_TIME_WINDOWS_TITLE = i18n.translate( } ); -export const SYNCHRONIZATION_LINK_LABEL = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.sources.synchronizationLinkLabel', - { - defaultMessage: 'Learn more about synchronization', - } -); - export const SYNCHRONIZATION_DISABLED_TITLE = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.sources.synchronizationDisabledTitle', { @@ -657,20 +650,6 @@ export const SYNCHRONIZATION_DISABLED_DESCRIPTION = i18n.translate( } ); -export const SYNC_FREQUENCY_LINK_LABEL = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.sources.syncFrequencyLinkLabel', - { - defaultMessage: 'Learn more about synchronization frequency', - } -); - -export const OBJECTS_AND_ASSETS_LINK_LABEL = i18n.translate( - 'xpack.enterpriseSearch.workplaceSearch.sources.objectsAndAssetsLinkLabel', - { - defaultMessage: 'Learn more about Objects and assets', - } -); - export const FULL_SYNC_LABEL = i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.sources.fullSyncLabel', { diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/box.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/box.svg new file mode 100644 index 0000000000000..b1b542eadd59c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/box.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/confluence_cloud.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/confluence_cloud.svg new file mode 100644 index 0000000000000..7aac36a6fe3c5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/confluence_cloud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/confluence_server.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/confluence_server.svg new file mode 100644 index 0000000000000..7aac36a6fe3c5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/confluence_server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/custom_api_source.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/custom_api_source.svg new file mode 100644 index 0000000000000..cc07fbbc50877 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/custom_api_source.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/dropbox.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/dropbox.svg new file mode 100644 index 0000000000000..01e5a7735de12 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/dropbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/github.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/github.svg new file mode 100644 index 0000000000000..aa9c3e5b45146 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/github_enterprise_server.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/github_enterprise_server.svg new file mode 100644 index 0000000000000..aa9c3e5b45146 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/github_enterprise_server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/gmail.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/gmail.svg new file mode 100644 index 0000000000000..31fe60c6a63f9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/gmail.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/google_drive.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/google_drive.svg new file mode 100644 index 0000000000000..f3fe82cd3cd98 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/google_drive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/jira_cloud.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/jira_cloud.svg new file mode 100644 index 0000000000000..c12e55798d889 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/jira_cloud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/jira_server.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/jira_server.svg new file mode 100644 index 0000000000000..4dfd0fd910079 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/jira_server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/onedrive.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/onedrive.svg new file mode 100644 index 0000000000000..c390dff1e418f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/onedrive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/salesforce.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/salesforce.svg new file mode 100644 index 0000000000000..ef6d552949424 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/salesforce.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/salesforce_sandbox.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/salesforce_sandbox.svg new file mode 100644 index 0000000000000..ef6d552949424 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/salesforce_sandbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/servicenow.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/servicenow.svg new file mode 100644 index 0000000000000..6388ec44d21d7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/servicenow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/sharepoint_online.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/sharepoint_online.svg new file mode 100644 index 0000000000000..aebfd7a8e49c0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/sharepoint_online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/slack.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/slack.svg new file mode 100644 index 0000000000000..8f6fc0c987eaa --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/slack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/public/assets/source_icons/zendesk.svg b/x-pack/plugins/enterprise_search/public/assets/source_icons/zendesk.svg new file mode 100644 index 0000000000000..8afd143dd9a7c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/source_icons/zendesk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts new file mode 100644 index 0000000000000..48909261243e8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -0,0 +1,304 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import type { HttpServiceSetup } from 'src/core/server'; + +import type { IntegrationCategory } from '../../../../src/plugins/custom_integrations/common'; +import type { CustomIntegrationsPluginSetup } from '../../../../src/plugins/custom_integrations/server'; + +interface WorkplaceSearchIntegration { + id: string; + title: string; + description: string; + categories: IntegrationCategory[]; + uiInternalPath?: string; +} + +const workplaceSearchIntegrations: WorkplaceSearchIntegration[] = [ + { + id: 'box', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.boxName', { + defaultMessage: 'Box', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.boxDescription', + { + defaultMessage: 'Search over your files and folders stored on Box with Workplace Search.', + } + ), + categories: ['document_storage'], + }, + { + id: 'confluence_cloud', + title: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.confluenceCloudName', + { + defaultMessage: 'Confluence Cloud', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.confluenceCloudDescription', + { + defaultMessage: + 'Search over your organizational content on Confluence Cloud with Workplace Search.', + } + ), + categories: ['knowledge_platform'], + }, + { + id: 'confluence_server', + title: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.confluenceServerName', + { + defaultMessage: 'Confluence Server', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.confluenceServerDescription', + { + defaultMessage: + 'Search over your organizational content on Confluence Server with Workplace Search.', + } + ), + categories: ['knowledge_platform'], + }, + { + id: 'dropbox', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.dropboxName', { + defaultMessage: 'Dropbox', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.dropboxDescription', + { + defaultMessage: + 'Search over your files and folders stored on Dropbox with Workplace Search.', + } + ), + categories: ['document_storage'], + }, + { + id: 'github', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.githubName', { + defaultMessage: 'GitHub', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.githubDescription', + { + defaultMessage: 'Search over your projects and repos on GitHub with Workplace Search.', + } + ), + categories: ['software_development'], + }, + { + id: 'github_enterprise_server', + title: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.githubEnterpriseServerName', + { + defaultMessage: 'GitHub Enterprise Server', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.githubEnterpriseServerDescription', + { + defaultMessage: + 'Search over your projects and repos on GitHub Enterprise Server with Workplace Search.', + } + ), + categories: ['software_development'], + }, + { + id: 'gmail', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.gmailName', { + defaultMessage: 'Gmail', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.gmailDescription', + { + defaultMessage: 'Search over your emails managed by Gmail with Workplace Search.', + } + ), + categories: ['communication'], + }, + { + id: 'google_drive', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.googleDriveName', { + defaultMessage: 'Google Drive', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.googleDriveDescription', + { + defaultMessage: 'Search over your documents on Google Drive with Workplace Search.', + } + ), + categories: ['document_storage'], + }, + { + id: 'jira_cloud', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.jiraCloudName', { + defaultMessage: 'Jira Cloud', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.jiraCloudDescription', + { + defaultMessage: 'Search over your project workflow on Jira Cloud with Workplace Search.', + } + ), + categories: ['project_management'], + }, + { + id: 'jira_server', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.jiraServerName', { + defaultMessage: 'Jira Server', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.jiraServerDescription', + { + defaultMessage: 'Search over your project workflow on Jira Server with Workplace Search.', + } + ), + categories: ['project_management'], + }, + { + id: 'onedrive', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.onedriveName', { + defaultMessage: 'OneDrive', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.onedriveDescription', + { + defaultMessage: 'Search over your files stored on OneDrive with Workplace Search.', + } + ), + categories: ['document_storage'], + uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/one_drive', + }, + { + id: 'salesforce', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.salesforceName', { + defaultMessage: 'Salesforce', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.salesforceDescription', + { + defaultMessage: 'Search over your content on Salesforce with Workplace Search.', + } + ), + categories: ['crm'], + }, + { + id: 'salesforce_sandbox', + title: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.salesforceSandboxName', + { + defaultMessage: 'Salesforce Sandbox', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.salesforceSandboxDescription', + { + defaultMessage: 'Search over your content on Salesforce Sandbox with Workplace Search.', + } + ), + categories: ['crm'], + }, + { + id: 'servicenow', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.servicenowName', { + defaultMessage: 'ServiceNow', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.servicenowDescription', + { + defaultMessage: 'Search over your content on ServiceNow with Workplace Search.', + } + ), + categories: ['enterprise_management'], + }, + { + id: 'sharepoint_online', + title: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.sharepointOnlineName', + { + defaultMessage: 'SharePoint Online', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.sharepointOnlineDescription', + { + defaultMessage: 'Search over your files stored on SharePoint Online with Workplace Search.', + } + ), + categories: ['document_storage'], + uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/share_point', + }, + { + id: 'slack', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.slackName', { + defaultMessage: 'Slack', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.slackDescription', + { + defaultMessage: 'Search over your messages on Slack with Workplace Search.', + } + ), + categories: ['communication'], + }, + { + id: 'zendesk', + title: i18n.translate('xpack.enterpriseSearch.workplaceSearch.integrations.zendeskName', { + defaultMessage: 'Zendesk', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.zendeskDescription', + { + defaultMessage: 'Search over your tickets on Zendesk with Workplace Search.', + } + ), + categories: ['customer_support'], + }, + { + id: 'custom_api_source', + title: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.customApiSourceName', + { + defaultMessage: 'Custom API Source', + } + ), + description: i18n.translate( + 'xpack.enterpriseSearch.workplaceSearch.integrations.customApiSourceDescription', + { + defaultMessage: + 'Search over anything by building your own integration with Workplace Search.', + } + ), + categories: ['custom'], + uiInternalPath: '/app/enterprise_search/workplace_search/sources/add/custom', + }, +]; + +export const registerEnterpriseSearchIntegrations = ( + http: HttpServiceSetup, + customIntegrations: CustomIntegrationsPluginSetup +) => { + workplaceSearchIntegrations.forEach((integration) => { + customIntegrations.registerCustomIntegration({ + uiInternalPath: `/app/enterprise_search/workplace_search/sources/add/${integration.id}`, + icons: [ + { + type: 'svg', + src: http.basePath.prepend( + `/plugins/enterpriseSearch/assets/source_icons/${integration.id}.svg` + ), + }, + ], + isBeta: false, + shipper: 'enterprise_search', + ...integration, + }); + }); +}; diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 26572134aa31c..1d58da5bbdd2b 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -15,6 +15,7 @@ import { KibanaRequest, DEFAULT_APP_CATEGORIES, } from '../../../../src/core/server'; +import { CustomIntegrationsPluginSetup } from '../../../../src/plugins/custom_integrations/server'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { InfraPluginSetup } from '../../infra/server'; @@ -31,6 +32,7 @@ import { import { registerTelemetryUsageCollector as registerASTelemetryUsageCollector } from './collectors/app_search/telemetry'; import { registerTelemetryUsageCollector as registerESTelemetryUsageCollector } from './collectors/enterprise_search/telemetry'; import { registerTelemetryUsageCollector as registerWSTelemetryUsageCollector } from './collectors/workplace_search/telemetry'; +import { registerEnterpriseSearchIntegrations } from './integrations'; import { checkAccess } from './lib/check_access'; import { entSearchHttpAgent } from './lib/enterprise_search_http_agent'; @@ -55,6 +57,7 @@ interface PluginsSetup { security?: SecurityPluginSetup; features: FeaturesPluginSetup; infra: InfraPluginSetup; + customIntegrations?: CustomIntegrationsPluginSetup; } interface PluginsStart { @@ -80,11 +83,15 @@ export class EnterpriseSearchPlugin implements Plugin { public setup( { capabilities, http, savedObjects, getStartServices }: CoreSetup, - { usageCollection, security, features, infra }: PluginsSetup + { usageCollection, security, features, infra, customIntegrations }: PluginsSetup ) { const config = this.config; const log = this.logger; + if (customIntegrations) { + registerEnterpriseSearchIntegrations(http, customIntegrations); + } + /* * Initialize config.ssl.certificateAuthorities file(s) - required for all API calls (+ access checks) */ diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts index aff9cd17dd001..d8ff7f1815a62 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts @@ -57,13 +57,21 @@ describe('log settings routes', () => { describe('validates', () => { it('validates good data', () => { - const request = { + mockRouter.shouldValidate({ body: { analytics: { enabled: true }, + }, + }); + mockRouter.shouldValidate({ + body: { api: { enabled: true }, }, - }; - mockRouter.shouldValidate(request); + }); + mockRouter.shouldValidate({ + body: { + crawler: { enabled: true }, + }, + }); }); it('rejects bad data', () => { diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts index b83dfd9d9ef15..2d0ce1411761a 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts @@ -38,6 +38,11 @@ export function registerSettingsRoutes({ enabled: schema.boolean(), }) ), + crawler: schema.maybe( + schema.object({ + enabled: schema.boolean(), + }) + ), }), }, }, diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.test.ts index 67edcc356e902..4374bbe88e183 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.test.ts @@ -92,6 +92,7 @@ describe('source engine routes', () => { it('creates a request to enterprise search', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/as/engines/:name/source_engines/bulk_create', + hasJsonResponse: false, }); }); }); @@ -145,6 +146,7 @@ describe('source engine routes', () => { it('creates a request to enterprise search', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/as/engines/:name/source_engines/:source_engine_name', + hasJsonResponse: false, }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.ts index 1d41a82287b0e..79be0c9c29322 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/source_engines.ts @@ -45,6 +45,7 @@ export function registerSourceEnginesRoutes({ }, enterpriseSearchRequestHandler.createRequest({ path: '/as/engines/:name/source_engines/bulk_create', + hasJsonResponse: false, }) ); @@ -60,6 +61,7 @@ export function registerSourceEnginesRoutes({ }, enterpriseSearchRequestHandler.createRequest({ path: '/as/engines/:name/source_engines/:source_engine_name', + hasJsonResponse: false, }) ); } diff --git a/x-pack/plugins/event_log/server/index.ts b/x-pack/plugins/event_log/server/index.ts index deeee970ce68a..b643060415b19 100644 --- a/x-pack/plugins/event_log/server/index.ts +++ b/x-pack/plugins/event_log/server/index.ts @@ -33,6 +33,7 @@ export const config: PluginConfigDescriptor = { settings?.xpack?.eventLog?.enabled === true ) { addDeprecation({ + configPath: 'xpack.eventLog.enabled', message: `"xpack.eventLog.enabled" is deprecated. The ability to disable this plugin will be removed in 8.0.0.`, correctiveActions: { manualSteps: [`Remove "xpack.eventLog.enabled" from your kibana configs.`], diff --git a/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx b/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx index 8ced172e696aa..0c46e1af301cf 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/layouts/default.tsx @@ -5,20 +5,12 @@ * 2.0. */ import React, { memo } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiImage, EuiSpacer, EuiText, EuiLink } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; - -import styled, { useTheme } from 'styled-components'; - -import type { EuiTheme } from 'src/plugins/kibana_react/common'; - import { useLink } from '../../../hooks'; import type { Section } from '../sections'; -import { useLinks, useStartServices } from '../hooks'; - import { WithHeaderLayout } from './'; interface Props { @@ -26,45 +18,11 @@ interface Props { children?: React.ReactNode; } -const Illustration = styled(EuiImage)` - margin-bottom: -77px; - position: relative; - top: -16px; - width: 395px; -`; - -const Hero = styled.div` - text-align: right; -`; - -const HeroImage = memo(() => { - const { toSharedAssets } = useLinks(); - const theme = useTheme() as EuiTheme; - const IS_DARK_THEME = theme.darkMode; - - return ( - - - - ); -}); - export const DefaultLayout: React.FunctionComponent = memo(({ section, children }) => { const { getHref } = useLink(); - const { docLinks } = useStartServices(); return ( } leftColumn={ @@ -79,20 +37,11 @@ export const DefaultLayout: React.FunctionComponent = memo(({ section, ch - +

- {i18n.translate('xpack.fleet.epm.pageSubtitleLinkText', { - defaultMessage: 'Getting started with Elastic Stack', - })} - - ), - }} + defaultMessage="Choose an integration to start collecting and analyzing your data" />

diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx index 94370587ddec8..86bac94bc50cd 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.stories.tsx @@ -22,7 +22,7 @@ export default { type Args = Omit & { width: number }; const args: Args = { - width: 250, + width: 280, title: 'Title', description: 'Description', name: 'beats', diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx index a68499dbd8dd0..091eb4c97183d 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx @@ -7,7 +7,7 @@ import React from 'react'; import styled from 'styled-components'; -import { EuiCard } from '@elastic/eui'; +import { EuiCard, EuiFlexItem, EuiBadge, EuiToolTip, EuiSpacer } from '@elastic/eui'; import { CardIcon } from '../../../../../components/package_icon'; import type { IntegrationCardItem } from '../../../../../../common/types/models/epm'; @@ -16,10 +16,10 @@ import { RELEASE_BADGE_DESCRIPTION, RELEASE_BADGE_LABEL } from './release_badge' export type PackageCardProps = IntegrationCardItem; -// adding the `href` causes EuiCard to use a `a` instead of a `button` -// `a` tags use `euiLinkColor` which results in blueish Badge text +// Min-height is roughly 3 lines of content. +// This keeps the cards from looking overly unbalanced because of content differences. const Card = styled(EuiCard)` - color: inherit; + min-height: 127px; `; export function PackageCard({ @@ -32,14 +32,28 @@ export function PackageCard({ url, release, }: PackageCardProps) { - const betaBadgeLabel = release && release !== 'ga' ? RELEASE_BADGE_LABEL[release] : undefined; - const betaBadgeLabelTooltipContent = - release && release !== 'ga' ? RELEASE_BADGE_DESCRIPTION[release] : undefined; + let releaseBadge: React.ReactNode | null = null; + + if (release && release !== 'ga') { + releaseBadge = ( + + + + + {RELEASE_BADGE_LABEL[release]} + + + + ); + } return ( } href={url} - betaBadgeLabel={betaBadgeLabel} - betaBadgeTooltipContent={betaBadgeLabelTooltipContent} target={url.startsWith('http') || url.startsWith('https') ? '_blank' : undefined} - /> + > + {releaseBadge} + ); } diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx index 2a4cb84f1e6ce..00adb2a7b4ffb 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import type { ReactNode } from 'react'; -import React, { Fragment, useCallback, useState } from 'react'; +import type { ReactNode, FunctionComponent } from 'react'; +import React, { useCallback, useState, useRef, useEffect } from 'react'; import { EuiFlexGrid, EuiFlexGroup, @@ -20,7 +20,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { useStartServices } from '../../../../../hooks'; import { Loading } from '../../../components'; import { useLocalSearch, searchIdField } from '../../../hooks'; @@ -31,7 +30,7 @@ import { PackageCard } from './package_card'; export interface Props { isLoading?: boolean; controls?: ReactNode | ReactNode[]; - title: string; + title?: string; list: IntegrationCardItem[]; initialSearch?: string; setSelectedCategory: (category: string) => void; @@ -40,7 +39,7 @@ export interface Props { callout?: JSX.Element | null; } -export function PackageListGrid({ +export const PackageListGrid: FunctionComponent = ({ isLoading, controls, title, @@ -50,9 +49,23 @@ export function PackageListGrid({ setSelectedCategory, showMissingIntegrationMessage = false, callout, -}: Props) { +}) => { const [searchTerm, setSearchTerm] = useState(initialSearch || ''); const localSearchRef = useLocalSearch(list); + const menuRef = useRef(null); + const [isSticky, setIsSticky] = useState(false); + const [windowScrollY] = useState(window.scrollY); + + useEffect(() => { + const menuRefCurrent = menuRef.current; + const onScroll = () => { + if (menuRefCurrent) { + setIsSticky(menuRefCurrent?.getBoundingClientRect().top < 110); + } + }; + window.addEventListener('scroll', onScroll); + return () => window.removeEventListener('scroll', onScroll); + }, [windowScrollY, isSticky]); const onQueryChange = ({ queryText: userInput, @@ -71,7 +84,7 @@ export function PackageListGrid({ setSearchTerm(''); }; - const controlsContent = ; + const controlsContent = ; let gridContent: JSX.Element; if (isLoading || !localSearchRef.current) { @@ -93,58 +106,68 @@ export function PackageListGrid({ } return ( - - {controlsContent} - - - {callout ? ( - <> - - {callout} - - ) : null} - - {gridContent} - {showMissingIntegrationMessage && ( - <> - - - - )} - - +
+ + + {controlsContent} + + + + {callout ? ( + <> + + {callout} + + ) : null} + + {gridContent} + {showMissingIntegrationMessage && ( + <> + + + + )} + + +
); -} +}; interface ControlsColumnProps { controls: ReactNode; - title: string; + title: string | undefined; + sticky: boolean; } -function ControlsColumn({ controls, title }: ControlsColumnProps) { +function ControlsColumn({ controls, title, sticky }: ControlsColumnProps) { + let titleContent; + if (title) { + titleContent = ( + <> + +

{title}

+
+ + + ); + } return ( - - -

{title}

-
- - - {controls} - - -
+ + {titleContent} + {controls} + ); } @@ -196,20 +219,17 @@ function MissingIntegrationContent({ resetQuery, setSelectedCategory, }: MissingIntegrationContentProps) { - const { - application: { getUrlForApp }, - } = useStartServices(); const handleCustomInputsLinkClick = useCallback(() => { resetQuery(); setSelectedCategory('custom'); }, [resetQuery, setSelectedCategory]); return ( - +

@@ -227,14 +247,6 @@ function MissingIntegrationContent({ /> ), - beatsTutorialLink: ( - - - - ), }} />

diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx index 40f89346c25bf..91b557d0db5b6 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx @@ -7,9 +7,8 @@ import React, { memo, useMemo, useState } from 'react'; import { useLocation, useHistory, useParams } from 'react-router-dom'; -import { i18n } from '@kbn/i18n'; import _ from 'lodash'; -import { EuiHorizontalRule } from '@elastic/eui'; +import { EuiHorizontalRule, EuiFlexItem } from '@elastic/eui'; import { pagePathGetters } from '../../../../constants'; import { @@ -93,10 +92,6 @@ const packageListToIntegrationsList = (packages: PackageList): PackageList => { }, []); }; -const title = i18n.translate('xpack.fleet.epmList.allTitle', { - defaultMessage: 'Browse by category', -}); - // TODO: clintandrewhall - this component is hard to test due to the hooks, particularly those that use `http` // or `location` to load data. Ideally, we'll split this into "connected" and "pure" components. export const AvailablePackages: React.FC = memo(() => { @@ -121,9 +116,7 @@ export const AvailablePackages: React.FC = memo(() => { function setSearchTerm(search: string) { // Use .replace so the browser's back button is not tied to single keystroke - history.replace( - pagePathGetters.integrations_all({ category: selectedCategory, searchTerm: search })[1] - ); + history.replace(pagePathGetters.integrations_all({ searchTerm: search })[1]); } const { data: eprPackages, isLoading: isLoadingAllPackages } = useGetPackages({ @@ -186,20 +179,26 @@ export const AvailablePackages: React.FC = memo(() => { } let controls = [ - , - , + + + , + , ]; if (categories) { controls = [ - { - setSelectedCategory(id); - }} - />, + + { + setSelectedCategory(id); + }} + /> + , ...controls, ]; } @@ -214,7 +213,6 @@ export const AvailablePackages: React.FC = memo(() => { return ( { const { icons } = props; if (icons && icons.length === 1 && icons[0].type === 'eui') { - return ; + return ; } else if (icons && icons.length === 1 && icons[0].type === 'svg') { - return ; + return ; } else { return ; } diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 897f160810e29..cc754b87686e6 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -89,6 +89,7 @@ export const config: PluginConfigDescriptor = { delete fullConfig.xpack.fleet.agents.elasticsearch.host; fullConfig.xpack.fleet.agents.elasticsearch.hosts = [oldValue]; addDeprecation({ + configPath: 'xpack.fleet.agents.elasticsearch.host', message: `Config key [xpack.fleet.agents.elasticsearch.host] is deprecated and replaced by [xpack.fleet.agents.elasticsearch.hosts]`, correctiveActions: { manualSteps: [ diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index a706ca6a54fdc..697ea0fa30d69 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -57,7 +57,7 @@ import { registerPreconfigurationRoutes, } from './routes'; -import type { ExternalCallback } from './types'; +import type { ExternalCallback, FleetRequestHandlerContext } from './types'; import type { ESIndexPatternService, AgentService, @@ -231,7 +231,21 @@ export class FleetPlugin }); } - const router = core.http.createRouter(); + core.http.registerRouteHandlerContext( + 'fleet', + (coreContext, request) => ({ + epm: { + // Use a lazy getter to avoid constructing this client when not used by a request handler + get internalSoClient() { + return appContextService + .getSavedObjects() + .getScopedClient(request, { excludedWrappers: ['security'] }); + }, + }, + }) + ); + + const router = core.http.createRouter(); // Register usage collection registerFleetUsageCollector(core, config, deps.usageCollection); diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index 2324d1a423bfc..c98038427cafc 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -9,7 +9,7 @@ import path from 'path'; import type { TypeOf } from '@kbn/config-schema'; import mime from 'mime-types'; -import type { RequestHandler, ResponseHeaders, KnownHeaders } from 'src/core/server'; +import type { ResponseHeaders, KnownHeaders } from 'src/core/server'; import type { GetInfoResponse, @@ -34,6 +34,7 @@ import type { DeletePackageRequestSchema, BulkUpgradePackagesFromRegistryRequestSchema, GetStatsRequestSchema, + FleetRequestHandler, UpdatePackageRequestSchema, } from '../../types'; import { @@ -57,7 +58,7 @@ import { getAsset } from '../../services/epm/archive/storage'; import { getPackageUsageStats } from '../../services/epm/packages/get'; import { updatePackage } from '../../services/epm/packages/update'; -export const getCategoriesHandler: RequestHandler< +export const getCategoriesHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { @@ -72,12 +73,12 @@ export const getCategoriesHandler: RequestHandler< } }; -export const getListHandler: RequestHandler< +export const getListHandler: FleetRequestHandler< undefined, TypeOf > = async (context, request, response) => { try { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = context.fleet.epm.internalSoClient; const res = await getPackages({ savedObjectsClient, ...request.query, @@ -93,9 +94,9 @@ export const getListHandler: RequestHandler< } }; -export const getLimitedListHandler: RequestHandler = async (context, request, response) => { +export const getLimitedListHandler: FleetRequestHandler = async (context, request, response) => { try { - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = context.fleet.epm.internalSoClient; const res = await getLimitedPackages({ savedObjectsClient }); const body: GetLimitedPackagesResponse = { response: res, @@ -108,110 +109,105 @@ export const getLimitedListHandler: RequestHandler = async (context, request, re } }; -export const getFileHandler: RequestHandler> = async ( - context, - request, - response -) => { - try { - const { pkgName, pkgVersion, filePath } = request.params; - const savedObjectsClient = context.core.savedObjects.client; - const installation = await getInstallation({ savedObjectsClient, pkgName }); - const useLocalFile = pkgVersion === installation?.version; +export const getFileHandler: FleetRequestHandler> = + async (context, request, response) => { + try { + const { pkgName, pkgVersion, filePath } = request.params; + const savedObjectsClient = context.fleet.epm.internalSoClient; + const installation = await getInstallation({ savedObjectsClient, pkgName }); + const useLocalFile = pkgVersion === installation?.version; + + if (useLocalFile) { + const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; + const fileBuffer = getArchiveEntry(assetPath); + // only pull local installation if we don't have it cached + const storedAsset = + !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath })); - if (useLocalFile) { - const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; - const fileBuffer = getArchiveEntry(assetPath); - // only pull local installation if we don't have it cached - const storedAsset = !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath })); + // error, if neither is available + if (!fileBuffer && !storedAsset) { + return response.custom({ + body: `installed package file not found: ${filePath}`, + statusCode: 404, + }); + } + + // if storedAsset is not available, fileBuffer *must* be + // b/c we error if we don't have at least one, and storedAsset is the least likely + const { buffer, contentType } = storedAsset + ? { + contentType: storedAsset.media_type, + buffer: storedAsset.data_utf8 + ? Buffer.from(storedAsset.data_utf8, 'utf8') + : Buffer.from(storedAsset.data_base64, 'base64'), + } + : { + contentType: mime.contentType(path.extname(assetPath)), + buffer: fileBuffer, + }; + + if (!contentType) { + return response.custom({ + body: `unknown content type for file: ${filePath}`, + statusCode: 400, + }); + } - // error, if neither is available - if (!fileBuffer && !storedAsset) { return response.custom({ - body: `installed package file not found: ${filePath}`, - statusCode: 404, + body: buffer, + statusCode: 200, + headers: { + 'cache-control': 'max-age=10, public', + 'content-type': contentType, + }, }); - } - - // if storedAsset is not available, fileBuffer *must* be - // b/c we error if we don't have at least one, and storedAsset is the least likely - const { buffer, contentType } = storedAsset - ? { - contentType: storedAsset.media_type, - buffer: storedAsset.data_utf8 - ? Buffer.from(storedAsset.data_utf8, 'utf8') - : Buffer.from(storedAsset.data_base64, 'base64'), + } else { + const registryResponse = await getFile(pkgName, pkgVersion, filePath); + const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; + const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { + const value = registryResponse.headers.get(knownHeader); + if (value !== null) { + headers[knownHeader] = value; } - : { - contentType: mime.contentType(path.extname(assetPath)), - buffer: fileBuffer, - }; + return headers; + }, {} as ResponseHeaders); - if (!contentType) { return response.custom({ - body: `unknown content type for file: ${filePath}`, - statusCode: 400, + body: registryResponse.body, + statusCode: registryResponse.status, + headers: proxiedHeaders, }); } - - return response.custom({ - body: buffer, - statusCode: 200, - headers: { - 'cache-control': 'max-age=10, public', - 'content-type': contentType, - }, - }); - } else { - const registryResponse = await getFile(pkgName, pkgVersion, filePath); - const headersToProxy: KnownHeaders[] = ['content-type', 'cache-control']; - const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { - const value = registryResponse.headers.get(knownHeader); - if (value !== null) { - headers[knownHeader] = value; - } - return headers; - }, {} as ResponseHeaders); - - return response.custom({ - body: registryResponse.body, - statusCode: registryResponse.status, - headers: proxiedHeaders, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } - } catch (error) { - return defaultIngestErrorHandler({ error, response }); - } -}; + }; -export const getInfoHandler: RequestHandler> = async ( - context, - request, - response -) => { - try { - const { pkgkey } = request.params; - const savedObjectsClient = context.core.savedObjects.client; - // TODO: change epm API to /packageName/version so we don't need to do this - const { pkgName, pkgVersion } = splitPkgKey(pkgkey); - const res = await getPackageInfo({ savedObjectsClient, pkgName, pkgVersion }); - const body: GetInfoResponse = { - response: res, - }; - return response.ok({ body }); - } catch (error) { - return defaultIngestErrorHandler({ error, response }); - } -}; +export const getInfoHandler: FleetRequestHandler> = + async (context, request, response) => { + try { + const { pkgkey } = request.params; + const savedObjectsClient = context.fleet.epm.internalSoClient; + // TODO: change epm API to /packageName/version so we don't need to do this + const { pkgName, pkgVersion } = splitPkgKey(pkgkey); + const res = await getPackageInfo({ savedObjectsClient, pkgName, pkgVersion }); + const body: GetInfoResponse = { + response: res, + }; + return response.ok({ body }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } + }; -export const updatePackageHandler: RequestHandler< +export const updatePackageHandler: FleetRequestHandler< TypeOf, unknown, TypeOf > = async (context, request, response) => { try { const { pkgkey } = request.params; - const savedObjectsClient = context.core.savedObjects.client; + const savedObjectsClient = context.fleet.epm.internalSoClient; const { pkgName } = splitPkgKey(pkgkey); @@ -226,30 +222,27 @@ export const updatePackageHandler: RequestHandler< } }; -export const getStatsHandler: RequestHandler> = async ( - context, - request, - response -) => { - try { - const { pkgName } = request.params; - const savedObjectsClient = context.core.savedObjects.client; - const body: GetStatsResponse = { - response: await getPackageUsageStats({ savedObjectsClient, pkgName }), - }; - return response.ok({ body }); - } catch (error) { - return defaultIngestErrorHandler({ error, response }); - } -}; +export const getStatsHandler: FleetRequestHandler> = + async (context, request, response) => { + try { + const { pkgName } = request.params; + const savedObjectsClient = context.fleet.epm.internalSoClient; + const body: GetStatsResponse = { + response: await getPackageUsageStats({ savedObjectsClient, pkgName }), + }; + return response.ok({ body }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); + } + }; -export const installPackageFromRegistryHandler: RequestHandler< +export const installPackageFromRegistryHandler: FleetRequestHandler< TypeOf, undefined, TypeOf > = async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asCurrentUser; + const savedObjectsClient = context.fleet.epm.internalSoClient; + const esClient = context.core.elasticsearch.client.asInternalUser; const { pkgkey } = request.params; const res = await installPackage({ @@ -284,13 +277,13 @@ const bulkInstallServiceResponseToHttpEntry = ( } }; -export const bulkInstallPackagesFromRegistryHandler: RequestHandler< +export const bulkInstallPackagesFromRegistryHandler: FleetRequestHandler< undefined, undefined, TypeOf > = async (context, request, response) => { - const savedObjectsClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asCurrentUser; + const savedObjectsClient = context.fleet.epm.internalSoClient; + const esClient = context.core.elasticsearch.client.asInternalUser; const bulkInstalledResponses = await bulkInstallPackages({ savedObjectsClient, esClient, @@ -303,7 +296,7 @@ export const bulkInstallPackagesFromRegistryHandler: RequestHandler< return response.ok({ body }); }; -export const installPackageByUploadHandler: RequestHandler< +export const installPackageByUploadHandler: FleetRequestHandler< undefined, undefined, TypeOf @@ -314,8 +307,8 @@ export const installPackageByUploadHandler: RequestHandler< body: { message: 'Requires Enterprise license' }, }); } - const savedObjectsClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asCurrentUser; + const savedObjectsClient = context.fleet.epm.internalSoClient; + const esClient = context.core.elasticsearch.client.asInternalUser; const contentType = request.headers['content-type'] as string; // from types it could also be string[] or undefined but this is checked later const archiveBuffer = Buffer.from(request.body); @@ -336,15 +329,15 @@ export const installPackageByUploadHandler: RequestHandler< } }; -export const deletePackageHandler: RequestHandler< +export const deletePackageHandler: FleetRequestHandler< TypeOf, undefined, TypeOf > = async (context, request, response) => { try { const { pkgkey } = request.params; - const savedObjectsClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asCurrentUser; + const savedObjectsClient = context.fleet.epm.internalSoClient; + const esClient = context.core.elasticsearch.client.asInternalUser; const res = await removeInstallation({ savedObjectsClient, pkgkey, diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts index 0f49b3cfa772d..360f2ec1d446e 100644 --- a/x-pack/plugins/fleet/server/routes/epm/index.ts +++ b/x-pack/plugins/fleet/server/routes/epm/index.ts @@ -8,6 +8,7 @@ import type { IRouter } from 'src/core/server'; import { PLUGIN_ID, EPM_API_ROUTES } from '../../constants'; +import type { FleetRequestHandlerContext } from '../../types'; import { GetCategoriesRequestSchema, GetPackagesRequestSchema, @@ -38,7 +39,7 @@ import { const MAX_FILE_SIZE_BYTES = 104857600; // 100MB -export const registerRoutes = (router: IRouter) => { +export const registerRoutes = (router: IRouter) => { router.get( { path: EPM_API_ROUTES.CATEGORIES_PATTERN, diff --git a/x-pack/plugins/fleet/server/routes/security.ts b/x-pack/plugins/fleet/server/routes/security.ts index 8efea34ed8164..33a510c27f04e 100644 --- a/x-pack/plugins/fleet/server/routes/security.ts +++ b/x-pack/plugins/fleet/server/routes/security.ts @@ -5,13 +5,13 @@ * 2.0. */ -import type { IRouter, RequestHandler } from 'src/core/server'; +import type { IRouter, RequestHandler, RequestHandlerContext } from 'src/core/server'; import { appContextService } from '../services'; -export function enforceSuperUser( - handler: RequestHandler -): RequestHandler { +export function enforceSuperUser( + handler: RequestHandler +): RequestHandler { return function enforceSuperHandler(context, req, res) { if (!appContextService.hasSecurity() || !appContextService.getSecurityLicense().isEnabled()) { return res.forbidden({ @@ -44,7 +44,9 @@ export function enforceSuperUser( }; } -export function makeRouterEnforcingSuperuser(router: IRouter): IRouter { +export function makeRouterEnforcingSuperuser( + router: IRouter +): IRouter { return { get: (options, handler) => router.get(options, enforceSuperUser(handler)), delete: (options, handler) => router.delete(options, enforceSuperUser(handler)), diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts index c196054faf08c..b39c6e7686110 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.test.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { httpServerMock } from 'src/core/server/mocks'; +import { httpServerMock, savedObjectsClientMock } from 'src/core/server/mocks'; import type { PostFleetSetupResponse } from '../../../common'; import { RegistryError } from '../../errors'; import { createAppContextStartContractMock, xpackMocks } from '../../mocks'; import { appContextService } from '../../services/app_context'; import { setupFleet } from '../../services/setup'; +import type { FleetRequestHandlerContext } from '../../types'; import { fleetSetupHandler } from './handlers'; @@ -24,12 +25,19 @@ jest.mock('../../services/setup', () => { const mockSetupFleet = setupFleet as jest.MockedFunction; describe('FleetSetupHandler', () => { - let context: ReturnType; + let context: FleetRequestHandlerContext; let response: ReturnType; let request: ReturnType; beforeEach(async () => { - context = xpackMocks.createRequestHandlerContext(); + context = { + ...xpackMocks.createRequestHandlerContext(), + fleet: { + epm: { + internalSoClient: savedObjectsClientMock.create(), + }, + }, + }; response = httpServerMock.createResponseFactory(); request = httpServerMock.createKibanaRequest({ method: 'post', diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.ts index d24db96667d52..c5b2ef0ade26f 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.ts @@ -12,6 +12,7 @@ import type { GetFleetStatusResponse, PostFleetSetupResponse } from '../../../co import { setupFleet } from '../../services/setup'; import { hasFleetServers } from '../../services/fleet_server'; import { defaultIngestErrorHandler } from '../../errors'; +import type { FleetRequestHandler } from '../../types'; export const getFleetStatusHandler: RequestHandler = async (context, request, response) => { try { @@ -42,10 +43,10 @@ export const getFleetStatusHandler: RequestHandler = async (context, request, re } }; -export const fleetSetupHandler: RequestHandler = async (context, request, response) => { +export const fleetSetupHandler: FleetRequestHandler = async (context, request, response) => { try { - const soClient = context.core.savedObjects.client; - const esClient = context.core.elasticsearch.client.asCurrentUser; + const soClient = context.fleet.epm.internalSoClient; + const esClient = context.core.elasticsearch.client.asInternalUser; const setupStatus = await setupFleet(soClient, esClient); const body: PostFleetSetupResponse = { ...setupStatus, diff --git a/x-pack/plugins/fleet/server/routes/setup/index.ts b/x-pack/plugins/fleet/server/routes/setup/index.ts index d64c9f24f2610..591b9c832172d 100644 --- a/x-pack/plugins/fleet/server/routes/setup/index.ts +++ b/x-pack/plugins/fleet/server/routes/setup/index.ts @@ -10,9 +10,11 @@ import type { IRouter } from 'src/core/server'; import { PLUGIN_ID, AGENTS_SETUP_API_ROUTES, SETUP_API_ROUTE } from '../../constants'; import type { FleetConfigType } from '../../../common'; +import type { FleetRequestHandlerContext } from '../../types/request_context'; + import { getFleetStatusHandler, fleetSetupHandler } from './handlers'; -export const registerFleetSetupRoute = (router: IRouter) => { +export const registerFleetSetupRoute = (router: IRouter) => { router.post( { path: SETUP_API_ROUTE, @@ -26,7 +28,7 @@ export const registerFleetSetupRoute = (router: IRouter) => { }; // That route is used by agent to setup Fleet -export const registerCreateFleetSetupRoute = (router: IRouter) => { +export const registerCreateFleetSetupRoute = (router: IRouter) => { router.post( { path: AGENTS_SETUP_API_ROUTES.CREATE_PATTERN, @@ -37,7 +39,7 @@ export const registerCreateFleetSetupRoute = (router: IRouter) => { ); }; -export const registerGetFleetStatusRoute = (router: IRouter) => { +export const registerGetFleetStatusRoute = (router: IRouter) => { router.get( { path: AGENTS_SETUP_API_ROUTES.INFO_PATTERN, @@ -48,7 +50,10 @@ export const registerGetFleetStatusRoute = (router: IRouter) => { ); }; -export const registerRoutes = (router: IRouter, config: FleetConfigType) => { +export const registerRoutes = ( + router: IRouter, + config: FleetConfigType +) => { // Ingest manager setup registerFleetSetupRoute(router); diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 63e6c277ed710..5bdd95ef0b874 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -96,3 +96,4 @@ export interface BulkActionResult { export * from './models'; export * from './rest_spec'; export * from './extensions'; +export { FleetRequestHandler, FleetRequestHandlerContext } from './request_context'; diff --git a/x-pack/plugins/fleet/server/types/request_context.ts b/x-pack/plugins/fleet/server/types/request_context.ts new file mode 100644 index 0000000000000..a3b414119b685 --- /dev/null +++ b/x-pack/plugins/fleet/server/types/request_context.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + KibanaResponseFactory, + RequestHandler, + RequestHandlerContext, + RouteMethod, + SavedObjectsClientContract, +} from '../../../../../src/core/server'; + +/** @internal */ +export interface FleetRequestHandlerContext extends RequestHandlerContext { + fleet: { + epm: { + /** + * Saved Objects client configured to use kibana_system privileges instead of end-user privileges. Should only be + * used by routes that have additional privilege checks for authorization (such as requiring superuser). + */ + readonly internalSoClient: SavedObjectsClientContract; + }; + }; +} + +/** + * Convenience type for request handlers in Fleet that includes the FleetRequestHandlerContext type + * @internal + */ +export type FleetRequestHandler< + P = unknown, + Q = unknown, + B = unknown, + Method extends RouteMethod = any, + ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory +> = RequestHandler; diff --git a/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx b/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx index 0e5ef1ca78033..2b0aaf5175b88 100644 --- a/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx +++ b/x-pack/plugins/infra/public/containers/with_kuery_autocompletion.tsx @@ -83,7 +83,6 @@ class WithKueryAutocompletionComponent extends React.Component< query: expression, selectionStart: cursorPosition, selectionEnd: cursorPosition, - // @ts-expect-error (until data service updates to new types) indexPatterns: [indexPattern], boolFilter: [], })) || []; diff --git a/x-pack/plugins/maps/server/index.ts b/x-pack/plugins/maps/server/index.ts index 4e5dfcf1f94c4..6a846bc245824 100644 --- a/x-pack/plugins/maps/server/index.ts +++ b/x-pack/plugins/maps/server/index.ts @@ -34,6 +34,7 @@ export const config: PluginConfigDescriptor = { return completeConfig; } addDeprecation({ + configPath: 'xpack.maps.showMapVisualizationTypes', message: i18n.translate('xpack.maps.deprecation.showMapVisualizationTypes.message', { defaultMessage: 'xpack.maps.showMapVisualizationTypes is deprecated and is no longer used', @@ -59,6 +60,7 @@ export const config: PluginConfigDescriptor = { return completeConfig; } addDeprecation({ + configPath: 'map.proxyElasticMapsServiceInMaps', documentationUrl: `https://www.elastic.co/guide/en/kibana/${branch}/maps-connect-to-ems.html#elastic-maps-server`, message: i18n.translate('xpack.maps.deprecation.proxyEMS.message', { defaultMessage: 'map.proxyElasticMapsServiceInMaps is deprecated and is no longer used', @@ -86,6 +88,7 @@ export const config: PluginConfigDescriptor = { return completeConfig; } addDeprecation({ + configPath: 'map.regionmap', message: i18n.translate('xpack.maps.deprecation.regionmap.message', { defaultMessage: 'map.regionmap is deprecated and is no longer used', }), diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/manifest.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/manifest.json index 123232935df05..f8feaef3be5f8 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/manifest.json +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/manifest.json @@ -1,29 +1,29 @@ { "id": "apm_transaction", "title": "APM", - "description": "Detect anomalies in transactions from your APM services for metric data.", + "description": "Detect anomalies in transactions from your APM services.", "type": "Transaction data", "logoFile": "logo.json", - "defaultIndexPattern": "apm-*-metric,metrics-apm*", + "defaultIndexPattern": "apm-*-transaction", "query": { "bool": { "filter": [ - { "term": { "processor.event": "metric" } }, - { "term": { "metricset.name": "transaction" } } + { "term": { "processor.event": "transaction" } }, + { "exists": { "field": "transaction.duration" } } ] } }, "jobs": [ { - "id": "apm_metrics", - "file": "apm_metrics.json" + "id": "high_mean_transaction_duration", + "file": "high_mean_transaction_duration.json" } ], "datafeeds": [ { - "id": "datafeed-apm_metrics", - "file": "datafeed_apm_metrics.json", - "job_id": "apm_metrics" + "id": "datafeed-high_mean_transaction_duration", + "file": "datafeed_high_mean_transaction_duration.json", + "job_id": "high_mean_transaction_duration" } ] } diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/apm_metrics.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/apm_metrics.json deleted file mode 100644 index d5092f3ffc553..0000000000000 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/apm_metrics.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "job_type": "anomaly_detector", - "groups": [ - "apm" - ], - "description": "Detects anomalies in transaction duration, throughput and error percentage for metric data.", - "analysis_config": { - "bucket_span": "15m", - "summary_count_field_name" : "doc_count", - "detectors" : [ - { - "detector_description" : "high duration by transaction type for an APM service", - "function" : "high_mean", - "field_name" : "transaction_duration", - "by_field_name" : "transaction.type", - "partition_field_name" : "service.name" - }, - { - "detector_description" : "transactions per minute for an APM service", - "function" : "mean", - "field_name" : "transactions_per_min", - "by_field_name" : "transaction.type", - "partition_field_name" : "service.name" - }, - { - "detector_description" : "percent failed for an APM service", - "function" : "high_mean", - "field_name" : "transaction_failure_percentage", - "by_field_name" : "transaction.type", - "partition_field_name" : "service.name" - } - ], - "influencers" : [ - "transaction.type", - "service.name" - ] - }, - "analysis_limits": { - "model_memory_limit": "32mb" - }, - "data_description": { - "time_field" : "@timestamp", - "time_format" : "epoch_ms" - }, - "model_plot_config": { - "enabled" : true, - "annotations_enabled" : true - }, - "results_index_name" : "custom-apm", - "custom_settings": { - "created_by": "ml-module-apm-transaction" - } -} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_apm_metrics.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_apm_metrics.json deleted file mode 100644 index ba45582252cd7..0000000000000 --- a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_apm_metrics.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "job_id": "JOB_ID", - "indices": [ - "INDEX_PATTERN_NAME" - ], - "chunking_config" : { - "mode" : "off" - }, - "query": { - "bool": { - "filter": [ - { "term": { "processor.event": "metric" } }, - { "term": { "metricset.name": "transaction" } } - ] - } - }, - "aggregations" : { - "buckets" : { - "composite" : { - "size" : 5000, - "sources" : [ - { - "date" : { - "date_histogram" : { - "field" : "@timestamp", - "fixed_interval" : "90s" - } - } - }, - { - "transaction.type" : { - "terms" : { - "field" : "transaction.type" - } - } - }, - { - "service.name" : { - "terms" : { - "field" : "service.name" - } - } - } - ] - }, - "aggs" : { - "@timestamp" : { - "max" : { - "field" : "@timestamp" - } - }, - "transactions_per_min" : { - "rate" : { - "unit" : "minute" - } - }, - "transaction_duration" : { - "avg" : { - "field" : "transaction.duration.histogram" - } - }, - "error_count" : { - "filter" : { - "term" : { - "event.outcome" : "failure" - } - }, - "aggs" : { - "actual_error_count" : { - "value_count" : { - "field" : "event.outcome" - } - } - } - }, - "success_count" : { - "filter" : { - "term" : { - "event.outcome" : "success" - } - } - }, - "transaction_failure_percentage" : { - "bucket_script" : { - "buckets_path" : { - "failure_count" : "error_count>_count", - "success_count" : "success_count>_count" - }, - "script" : "if ((params.failure_count + params.success_count)==0){return 0;}else{return params.failure_count/(params.failure_count + params.success_count);}" - } - } - } - } - } -} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_high_mean_transaction_duration.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_high_mean_transaction_duration.json new file mode 100644 index 0000000000000..d312577902f51 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/datafeed_high_mean_transaction_duration.json @@ -0,0 +1,14 @@ +{ + "job_id": "JOB_ID", + "indices": [ + "INDEX_PATTERN_NAME" + ], + "query": { + "bool": { + "filter": [ + { "term": { "processor.event": "transaction" } }, + { "exists": { "field": "transaction.duration.us" } } + ] + } + } +} diff --git a/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/high_mean_transaction_duration.json b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/high_mean_transaction_duration.json new file mode 100644 index 0000000000000..77284cb275cd8 --- /dev/null +++ b/x-pack/plugins/ml/server/models/data_recognizer/modules/apm_transaction/ml/high_mean_transaction_duration.json @@ -0,0 +1,35 @@ +{ + "job_type": "anomaly_detector", + "groups": [ + "apm" + ], + "description": "Detect transaction duration anomalies across transaction types for your APM services.", + "analysis_config": { + "bucket_span": "15m", + "detectors": [ + { + "detector_description": "high duration by transaction type for an APM service", + "function": "high_mean", + "field_name": "transaction.duration.us", + "by_field_name": "transaction.type", + "partition_field_name": "service.name" + } + ], + "influencers": [ + "transaction.type", + "service.name" + ] + }, + "analysis_limits": { + "model_memory_limit": "32mb" + }, + "data_description": { + "time_field": "@timestamp" + }, + "model_plot_config": { + "enabled": true + }, + "custom_settings": { + "created_by": "ml-module-apm-transaction" + } +} diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/beats_template.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/beats_template.tsx index 3bab01af8ceb7..7a070c735bbea 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/beats_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/beats_template.tsx @@ -23,6 +23,7 @@ export const BeatsTemplate: React.FC = ({ instance, ...props defaultMessage: 'Overview', }), route: '/beats', + testSubj: 'beatsOverviewPage', }); tabs.push({ id: 'instances', @@ -30,6 +31,7 @@ export const BeatsTemplate: React.FC = ({ instance, ...props defaultMessage: 'Instances', }), route: '/beats/beats', + testSubj: 'beatsListingPage', }); } else { tabs.push({ diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx index 489ad110c40fd..4611f17159621 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx @@ -71,12 +71,7 @@ export const BeatsInstancesPage: React.FC = ({ clusters }) => { ]); return ( - +
= ({ clusters }) => { }; return ( - -
{renderOverview(data)}
+ +
{renderOverview(data)}
); }; diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx index 444794d118b0f..2d2fe99758ff7 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/instance.tsx @@ -164,13 +164,8 @@ export const KibanaInstancePage: React.FC = ({ clusters }) => { }, [ccs, clusterUuid, instance, services.data?.query.timefilter.timefilter, services.http]); return ( - -
+ +
diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx index ae0237ea40472..076e9413216fb 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx @@ -87,12 +87,7 @@ export const KibanaInstancesPage: React.FC = ({ clusters }) => { ]); return ( - +
= ({ ...props }) => { defaultMessage: 'Overview', }), route: '/kibana', + testSubj: 'kibanaOverviewPage', }, { id: 'instances', @@ -23,6 +24,7 @@ export const KibanaTemplate: React.FC = ({ ...props }) => { defaultMessage: 'Instances', }), route: '/kibana/instances', + testSubj: 'kibanaInstancesPage', }, ]; diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx index a47da048e1936..4c480bf1fbb33 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/overview.tsx @@ -107,12 +107,7 @@ export const KibanaOverviewPage: React.FC = ({ clusters }) => { }, [ccs, clusterUuid, services.data?.query.timefilter.timefilter, services.http]); return ( - + ); diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx index 26072f53f4752..e798e7d74ad38 100644 --- a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx @@ -220,12 +220,10 @@ async function executeCheck(checker: SettingsChecker, http: { fetch: any }): Pro return { found, reason }; } catch (err: any) { - const { data } = err; - return { error: true, found: false, - errorReason: data, + errorReason: err.body, }; } } diff --git a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx index 5c030814d9cdf..23eeb2c034a80 100644 --- a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx @@ -92,7 +92,7 @@ export const PageTemplate: React.FC = ({ }; return ( -
+
diff --git a/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap index b6d117fb3269c..8852d104fe00a 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/__snapshots__/no_data.test.js.snap @@ -3,7 +3,13 @@ exports[`NoData should show a default message if reason is unknown 1`] = `
+

+ No monitoring data found. +

+

+ No monitoring data found. +

{ + return {children}; + }; + if (isCloudEnabled) { return ( - +

- + ); } if (useInternalCollection) { return ( - + + +

+ +

+
-
+ ); } return ( - + + +

+ +

+
-
+ ); } diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/we_tried.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/we_tried.test.js.snap index 482e6e97115b7..4e1209a5fd2aa 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/we_tried.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/__snapshots__/we_tried.test.js.snap @@ -1,15 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`WeTried should render "we tried" message 1`] = ` -Array [ +

We couldn't activate monitoring -

, +


, + />
@@ -19,6 +21,6 @@ Array [

If data is in your cluster, your monitoring dashboards will show up here.

-
, -] +
+
`; diff --git a/x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.js index 17e171451e3a3..37504f5842a74 100644 --- a/x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.js +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.js @@ -5,13 +5,13 @@ * 2.0. */ -import React, { Fragment } from 'react'; +import React from 'react'; import { EuiText, EuiHorizontalRule, EuiTitle } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; export function WeTried() { return ( - +

- +

); } diff --git a/x-pack/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html index c479b282cd778..fd14120e1db2f 100644 --- a/x-pack/plugins/monitoring/public/directives/main/index.html +++ b/x-pack/plugins/monitoring/public/directives/main/index.html @@ -1,4 +1,4 @@ -
+
diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 5e02767fea98a..4fe5d9c765734 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -107,7 +107,7 @@ describe('config schema', () => { "index": "metricbeat-*", }, "min_interval_seconds": 10, - "render_react_app": false, + "render_react_app": true, "show_license_expiration": true, }, } diff --git a/x-pack/plugins/monitoring/server/config.ts b/x-pack/plugins/monitoring/server/config.ts index 5c2bdc1424f93..ddbfd480a9f4e 100644 --- a/x-pack/plugins/monitoring/server/config.ts +++ b/x-pack/plugins/monitoring/server/config.ts @@ -51,7 +51,7 @@ export const configSchema = schema.object({ }), min_interval_seconds: schema.number({ defaultValue: 10 }), show_license_expiration: schema.boolean({ defaultValue: true }), - render_react_app: schema.boolean({ defaultValue: false }), + render_react_app: schema.boolean({ defaultValue: true }), }), kibana: schema.object({ collection: schema.object({ diff --git a/x-pack/plugins/monitoring/server/deprecations.ts b/x-pack/plugins/monitoring/server/deprecations.ts index 3554abd569581..9dec7b105f2f6 100644 --- a/x-pack/plugins/monitoring/server/deprecations.ts +++ b/x-pack/plugins/monitoring/server/deprecations.ts @@ -55,6 +55,7 @@ export const deprecations = ({ const legacyKey = get(config, `xpack.monitoring.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}`); if (emailNotificationsEnabled && !updatedKey && !legacyKey) { addDeprecation({ + configPath: `cluster_alerts.email_notifications.enabled`, message: `Config key [${fromPath}.${CLUSTER_ALERTS_ADDRESS_CONFIG_KEY}] will be required for email notifications to work in 8.0."`, correctiveActions: { manualSteps: [ @@ -70,6 +71,7 @@ export const deprecations = ({ if (es) { if (es.username === 'elastic') { addDeprecation({ + configPath: 'elasticsearch.username', message: `Setting [${fromPath}.username] to "elastic" is deprecated. You should use the "kibana_system" user instead.`, correctiveActions: { manualSteps: [`Replace [${fromPath}.username] from "elastic" to "kibana_system".`], @@ -77,6 +79,7 @@ export const deprecations = ({ }); } else if (es.username === 'kibana') { addDeprecation({ + configPath: 'elasticsearch.username', message: `Setting [${fromPath}.username] to "kibana" is deprecated. You should use the "kibana_system" user instead.`, correctiveActions: { manualSteps: [`Replace [${fromPath}.username] from "kibana" to "kibana_system".`], @@ -91,6 +94,7 @@ export const deprecations = ({ if (ssl) { if (ssl.key !== undefined && ssl.certificate === undefined) { addDeprecation({ + configPath: 'elasticsearch.ssl.key', message: `Setting [${fromPath}.key] without [${fromPath}.certificate] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`, correctiveActions: { manualSteps: [ @@ -100,6 +104,7 @@ export const deprecations = ({ }); } else if (ssl.certificate !== undefined && ssl.key === undefined) { addDeprecation({ + configPath: 'elasticsearch.ssl.certificate', message: `Setting [${fromPath}.certificate] without [${fromPath}.key] is deprecated. This has no effect, you should use both settings to enable TLS client authentication to Elasticsearch.`, correctiveActions: { manualSteps: [ diff --git a/x-pack/plugins/osquery/public/scheduled_query_groups/queries/ecs_mapping_editor_field.tsx b/x-pack/plugins/osquery/public/scheduled_query_groups/queries/ecs_mapping_editor_field.tsx index 34d7dd755e2b9..8a5fa0e2066f2 100644 --- a/x-pack/plugins/osquery/public/scheduled_query_groups/queries/ecs_mapping_editor_field.tsx +++ b/x-pack/plugins/osquery/public/scheduled_query_groups/queries/ecs_mapping_editor_field.tsx @@ -6,7 +6,7 @@ */ import { produce } from 'immer'; -import { isEmpty, find, orderBy, sortedUniqBy, isArray, map } from 'lodash'; +import { each, isEmpty, find, orderBy, sortedUniqBy, isArray, map, reduce, get } from 'lodash'; import React, { forwardRef, useCallback, @@ -624,30 +624,47 @@ export const ECSMappingEditorField = ({ field, query, fieldRef }: ECSMappingEdit return currentValue; } - const tablesOrderMap = - ast?.from?.value?.reduce( - ( - acc: { [x: string]: number }, - table: { value: { alias?: { value: string }; value: { value: string } } }, - index: number - ) => { - acc[table.value.alias?.value ?? table.value.value.value] = index; - return acc; - }, - {} - ) ?? {}; - - const astOsqueryTables: Record = + const astOsqueryTables: Record< + string, + { + columns: OsqueryColumn[]; + order: number; + } + > = ast?.from?.value?.reduce( ( - acc: { [x: string]: OsqueryColumn[] }, - table: { value: { alias?: { value: string }; value: { value: string } } } - ) => { - const osqueryTable = find(osquerySchema, ['name', table.value.value.value]); - - if (osqueryTable) { - acc[table.value.alias?.value ?? table.value.value.value] = osqueryTable.columns; + acc: { + [x: string]: { + columns: OsqueryColumn[]; + order: number; + }; + }, + table: { + value: { + left?: { value: { value: string }; alias?: { value: string } }; + right?: { value: { value: string }; alias?: { value: string } }; + value?: { value: string }; + alias?: { value: string }; + }; } + ) => { + each(['value.left', 'value.right', 'value'], (valueKey) => { + if (valueKey) { + const osqueryTable = find(osquerySchema, [ + 'name', + get(table, `${valueKey}.value.value`), + ]); + + if (osqueryTable) { + acc[ + get(table, `${valueKey}.alias.value`) ?? get(table, `${valueKey}.value.value`) + ] = { + columns: osqueryTable.columns, + order: Object.keys(acc).length, + }; + } + } + }); return acc; }, @@ -659,75 +676,107 @@ export const ECSMappingEditorField = ({ field, query, fieldRef }: ECSMappingEdit return currentValue; } - /* - Simple query - select * from users; - */ - if ( - ast?.selectItems?.value?.length && - ast?.selectItems?.value[0].value === '*' && - ast.from?.value?.length && - ast?.from.value[0].value?.value?.value && - astOsqueryTables[ast.from.value[0].value.value.value] - ) { - const tableName = - ast.from.value[0].value.alias?.value ?? ast.from.value[0].value.value.value; - - return astOsqueryTables[ast.from.value[0].value.value.value].map((osqueryColumn) => ({ - label: osqueryColumn.name, - value: { - name: osqueryColumn.name, - description: osqueryColumn.description, - table: tableName, - tableOrder: tablesOrderMap[tableName], - suggestion_label: osqueryColumn.name, - }, - })); - } - - /* - Advanced query - select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid; - */ const suggestions = isArray(ast?.selectItems?.value) && ast?.selectItems?.value - // @ts-expect-error update types - ?.map((selectItem) => { - const [table, column] = selectItem.value?.split('.'); - - if (column === '*' && astOsqueryTables[table]) { - return astOsqueryTables[table].map((osqueryColumn) => ({ - label: osqueryColumn.name, - value: { - name: osqueryColumn.name, - description: osqueryColumn.description, - table, - tableOrder: tablesOrderMap[table], - suggestion_label: `${osqueryColumn.name}`, - }, - })); - } + ?.map((selectItem: { type: string; value: string; hasAs: boolean; alias?: string }) => { + if (selectItem.type === 'Identifier') { + /* + select * from routes, uptime; + */ + if (ast?.selectItems?.value.length === 1 && selectItem.value === '*') { + return reduce( + astOsqueryTables, + (acc, { columns: osqueryColumns, order: tableOrder }, table) => { + acc.push( + ...osqueryColumns.map((osqueryColumn) => ({ + label: osqueryColumn.name, + value: { + name: osqueryColumn.name, + description: osqueryColumn.description, + table, + tableOrder, + suggestion_label: osqueryColumn.name, + }, + })) + ); + return acc; + }, + [] as OsquerySchemaOption[] + ); + } - if (astOsqueryTables && astOsqueryTables[table]) { - const osqueryColumn = find(astOsqueryTables[table], ['name', column]); - - if (osqueryColumn) { - const label = selectItem.hasAs ? selectItem.alias : column; - - return [ - { - label, - value: { - name: osqueryColumn.name, - description: osqueryColumn.description, - table, - tableOrder: tablesOrderMap[table], - suggestion_label: `${label}`, - }, + /* + select i.*, p.resident_size, p.user_time, p.system_time, time.minutes as counter from osquery_info i, processes p, time where p.pid = i.pid; + */ + + const [table, column] = selectItem.value.includes('.') + ? selectItem.value?.split('.') + : [Object.keys(astOsqueryTables)[0], selectItem.value]; + + if (column === '*' && astOsqueryTables[table]) { + const { columns: osqueryColumns, order: tableOrder } = astOsqueryTables[table]; + return osqueryColumns.map((osqueryColumn) => ({ + label: osqueryColumn.name, + value: { + name: osqueryColumn.name, + description: osqueryColumn.description, + table, + tableOrder, + suggestion_label: `${osqueryColumn.name}`, }, - ]; + })); } + + if (astOsqueryTables[table]) { + const osqueryColumn = find(astOsqueryTables[table].columns, ['name', column]); + + if (osqueryColumn) { + const label = selectItem.hasAs ? selectItem.alias : column; + + return [ + { + label, + value: { + name: osqueryColumn.name, + description: osqueryColumn.description, + table, + tableOrder: astOsqueryTables[table].order, + suggestion_label: `${label}`, + }, + }, + ]; + } + } + } + + /* + SELECT pid, uid, name, ROUND(( + (user_time + system_time) / (cpu_time.tsb - cpu_time.itsb) + ) * 100, 2) AS percentage + FROM processes, ( + SELECT ( + SUM(user) + SUM(nice) + SUM(system) + SUM(idle) * 1.0) AS tsb, + SUM(COALESCE(idle, 0)) + SUM(COALESCE(iowait, 0)) AS itsb + FROM cpu_time + ) AS cpu_time + ORDER BY user_time+system_time DESC + LIMIT 5; + */ + + if (selectItem.type === 'FunctionCall' && selectItem.hasAs) { + return [ + { + label: selectItem.alias, + value: { + name: selectItem.alias, + description: '', + table: '', + tableOrder: -1, + suggestion_label: selectItem.alias, + }, + }, + ]; } return []; diff --git a/x-pack/plugins/reporting/server/config/index.ts b/x-pack/plugins/reporting/server/config/index.ts index a45c084131a4d..1eeafb4e0c513 100644 --- a/x-pack/plugins/reporting/server/config/index.ts +++ b/x-pack/plugins/reporting/server/config/index.ts @@ -25,6 +25,7 @@ export const config: PluginConfigDescriptor = { const reporting = get(settings, fromPath); if (reporting?.index) { addDeprecation({ + configPath: `${fromPath}.index`, title: i18n.translate('xpack.reporting.deprecations.reportingIndex.title', { defaultMessage: 'Setting "{fromPath}.index" is deprecated', values: { fromPath }, @@ -51,6 +52,7 @@ export const config: PluginConfigDescriptor = { if (reporting?.roles?.enabled !== false) { addDeprecation({ + configPath: `${fromPath}.roles.enabled`, level: 'warning', title: i18n.translate('xpack.reporting.deprecations.reportingRoles.title', { defaultMessage: 'Setting "{fromPath}.roles" is deprecated', diff --git a/x-pack/plugins/security/server/config_deprecations.test.ts b/x-pack/plugins/security/server/config_deprecations.test.ts index 634df081d77d7..d4b18a2e1b296 100644 --- a/x-pack/plugins/security/server/config_deprecations.test.ts +++ b/x-pack/plugins/security/server/config_deprecations.test.ts @@ -17,6 +17,7 @@ const deprecationContext = configDeprecationsMock.createContext(); const applyConfigDeprecations = (settings: Record = {}) => { const deprecations = securityConfigDeprecationProvider(configDeprecationFactory); const deprecationMessages: string[] = []; + const configPaths: string[] = []; const { config: migrated } = applyDeprecations( settings, deprecations.map((deprecation) => ({ @@ -25,10 +26,13 @@ const applyConfigDeprecations = (settings: Record = {}) => { context: deprecationContext, })), () => - ({ message }) => - deprecationMessages.push(message) + ({ message, configPath }) => { + deprecationMessages.push(message); + configPaths.push(configPath); + } ); return { + configPaths, messages: deprecationMessages, migrated, }; @@ -356,12 +360,14 @@ describe('Config Deprecations', () => { }, }, }; - const { messages } = applyConfigDeprecations(cloneDeep(config)); + const { messages, configPaths } = applyConfigDeprecations(cloneDeep(config)); expect(messages).toMatchInlineSnapshot(` Array [ "\\"xpack.security.authc.providers.saml..maxRedirectURLSize\\" is no longer used.", ] `); + + expect(configPaths).toEqual(['xpack.security.authc.providers.saml.saml1.maxRedirectURLSize']); }); it(`warns when 'xpack.security.authc.providers' is an array of strings`, () => { diff --git a/x-pack/plugins/security/server/config_deprecations.ts b/x-pack/plugins/security/server/config_deprecations.ts index ce9eb76fb1dc8..c8c8e64648c4b 100644 --- a/x-pack/plugins/security/server/config_deprecations.ts +++ b/x-pack/plugins/security/server/config_deprecations.ts @@ -35,6 +35,7 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ const legacyAuditLoggerEnabled = !settings?.xpack?.security?.audit?.appender; if (auditLoggingEnabled && legacyAuditLoggerEnabled) { addDeprecation({ + configPath: 'xpack.security.audit.appender', title: i18n.translate('xpack.security.deprecations.auditLoggerTitle', { defaultMessage: 'The legacy audit logger is deprecated', }), @@ -59,6 +60,7 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ (settings, fromPath, addDeprecation) => { if (Array.isArray(settings?.xpack?.security?.authc?.providers)) { addDeprecation({ + configPath: 'xpack.security.authc.providers', title: i18n.translate('xpack.security.deprecations.authcProvidersTitle', { defaultMessage: 'Defining "xpack.security.authc.providers" as an array of provider types is deprecated', @@ -92,6 +94,7 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ if (hasProviderType('basic') && hasProviderType('token')) { addDeprecation({ + configPath: 'xpack.security.authc.providers', title: i18n.translate('xpack.security.deprecations.basicAndTokenProvidersTitle', { defaultMessage: 'Both "basic" and "token" authentication providers are enabled in "xpack.security.authc.providers"', @@ -119,8 +122,13 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ string, any >; - if (Object.values(samlProviders).find((provider) => !!provider.maxRedirectURLSize)) { + + const foundProvider = Object.entries(samlProviders).find( + ([_, provider]) => !!provider.maxRedirectURLSize + ); + if (foundProvider) { addDeprecation({ + configPath: `xpack.security.authc.providers.saml.${foundProvider[0]}.maxRedirectURLSize`, title: i18n.translate('xpack.security.deprecations.maxRedirectURLSizeTitle', { defaultMessage: '"xpack.security.authc.providers.saml..maxRedirectURLSize" is deprecated', @@ -143,6 +151,7 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ (settings, fromPath, addDeprecation, { branch }) => { if ('enabled' in (settings?.xpack?.security || {})) { addDeprecation({ + configPath: 'xpack.security.enabled', title: i18n.translate('xpack.security.deprecations.enabledTitle', { defaultMessage: 'Setting "xpack.security.enabled" is deprecated', }), @@ -169,6 +178,7 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ (settings, fromPath, addDeprecation, { branch }) => { if (settings?.xpack?.security?.session?.idleTimeout === undefined) { addDeprecation({ + configPath: 'xpack.security.session.idleTimeout', level: 'warning', title: i18n.translate('xpack.security.deprecations.idleTimeoutTitle', { defaultMessage: 'The "xpack.security.session.idleTimeout" default is changing', @@ -192,6 +202,7 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ if (settings?.xpack?.security?.session?.lifespan === undefined) { addDeprecation({ + configPath: 'xpack.security.session.lifespan', level: 'warning', title: i18n.translate('xpack.security.deprecations.lifespanTitle', { defaultMessage: 'The "xpack.security.session.lifespan" default is changing', diff --git a/x-pack/plugins/security_solution/common/detection_engine/utils.ts b/x-pack/plugins/security_solution/common/detection_engine/utils.ts index 28749e0400bbb..7d4badcd3507c 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/utils.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/utils.ts @@ -51,7 +51,8 @@ export const normalizeThresholdField = ( ? thresholdField : isEmpty(thresholdField) ? [] - : [thresholdField!]; + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + [thresholdField!]; }; export const normalizeThresholdObject = (threshold: Threshold): ThresholdNormalized => { diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts index d7ab014c3b445..fdb8416de2ed8 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_endpoint_hosts.ts @@ -161,7 +161,7 @@ export async function indexEndpointHostDocs({ const indexedAgentResponse = await indexFleetAgentForHost( client, kbnClient, - hostMetadata!, + hostMetadata, realPolicies[appliedPolicyId].policy_id, kibanaVersion ); diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_agent.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_agent.ts index b792b34dd510f..67a261d088f86 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_agent.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_agent.ts @@ -73,7 +73,7 @@ export const indexFleetAgentForHost = async ( .index({ index: agentDoc._index, id: agentDoc._id, - body: agentDoc._source!, + body: agentDoc._source, op_type: 'create', }) .catch(wrapErrorAndRejectPromise); diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts index a53e37f363d05..287d86c6fba9e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts @@ -89,11 +89,11 @@ describe('Cases connectors', () => { addServiceNowConnector(snConnector); cy.wait('@createConnector').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); + cy.wrap(response?.statusCode).should('eql', 200); cy.get(TOASTER).should('have.text', "Created 'New connector'"); cy.get(TOASTER).should('not.exist'); - selectLastConnectorCreated(response!.body.id); + selectLastConnectorCreated(response?.body.id); cy.wait('@saveConnector', { timeout: 10000 }).its('response.statusCode').should('eql', 200); cy.get(SERVICE_NOW_MAPPING).first().should('have.text', 'short_description'); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts index a9a592c7803fe..79b053874f212 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/custom_query_rule.spec.ts @@ -351,10 +351,10 @@ describe('Custom detection rules deletion and edition', () => { goToRuleDetails(); cy.wait('@fetchRuleDetails').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); + cy.wrap(response?.statusCode).should('eql', 200); - cy.wrap(response!.body.max_signals).should('eql', getExistingRule().maxSignals); - cy.wrap(response!.body.enabled).should('eql', false); + cy.wrap(response?.body.max_signals).should('eql', getExistingRule().maxSignals); + cy.wrap(response?.body.enabled).should('eql', false); }); }); @@ -415,9 +415,9 @@ describe('Custom detection rules deletion and edition', () => { saveEditedRule(); cy.wait('@getRule').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); + cy.wrap(response?.statusCode).should('eql', 200); // ensure that editing rule does not modify max_signals - cy.wrap(response!.body.max_signals).should('eql', getExistingRule().maxSignals); + cy.wrap(response?.body.max_signals).should('eql', getExistingRule().maxSignals); }); cy.get(RULE_NAME_HEADER).should('contain', `${getEditedRule().name}`); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts index 03086810a8435..18b7b122f0967 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/export_rule.spec.ts @@ -35,7 +35,7 @@ describe('Export rules', () => { goToManageAlertsDetectionRules(); exportFirstRule(); cy.wait('@export').then(({ response }) => { - cy.wrap(response!.body).should('eql', expectedExportedRule(this.ruleResponse)); + cy.wrap(response?.body).should('eql', expectedExportedRule(this.ruleResponse)); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts index 48269677466b6..71bbea673e5c8 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/creation.spec.ts @@ -70,7 +70,7 @@ describe('Timeline Templates', () => { addNameToTimeline(getTimeline().title); cy.wait('@timeline').then(({ response }) => { - const timelineId = response!.body.data.persistTimeline.timeline.savedObjectId; + const timelineId = response?.body.data.persistTimeline.timeline.savedObjectId; addDescriptionToTimeline(getTimeline().description); addNotesToTimeline(getTimeline().notes); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts index a20baffeadc7e..638a70df35850 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_templates/export.spec.ts @@ -35,9 +35,9 @@ describe('Export timelines', () => { exportTimeline(this.templateId); cy.wait('@export').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); + cy.wrap(response?.statusCode).should('eql', 200); - cy.wrap(response!.body).should( + cy.wrap(response?.body).should( 'eql', expectedExportedTimelineTemplate(this.templateResponse) ); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts index 029216f701ae0..29d1cb588f1a2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/export.spec.ts @@ -33,9 +33,9 @@ describe('Export timelines', () => { exportTimeline(this.timelineId); cy.wait('@export').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); + cy.wrap(response?.statusCode).should('eql', 200); - cy.wrap(response!.body).should('eql', expectedExportedTimeline(this.timelineResponse)); + cy.wrap(response?.body).should('eql', expectedExportedTimeline(this.timelineResponse)); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/search_or_filter.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/search_or_filter.spec.ts index 9d019cf23ebb1..b42bdcdd6fb8d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/search_or_filter.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/search_or_filter.spec.ts @@ -54,8 +54,8 @@ describe('Update kqlMode for timeline', () => { it('should be able to update timeline kqlMode with filter', () => { cy.get(TIMELINE_KQLMODE_FILTER).click(); cy.wait('@update').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); - cy.wrap(response!.body.data.persistTimeline.timeline.kqlMode).should('eql', 'filter'); + cy.wrap(response?.statusCode).should('eql', 200); + cy.wrap(response?.body.data.persistTimeline.timeline.kqlMode).should('eql', 'filter'); cy.get(ADD_FILTER).should('exist'); }); }); @@ -63,8 +63,8 @@ describe('Update kqlMode for timeline', () => { it('should be able to update timeline kqlMode with search', () => { cy.get(TIMELINE_KQLMODE_SEARCH).click(); cy.wait('@update').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); - cy.wrap(response!.body.data.persistTimeline.timeline.kqlMode).should('eql', 'search'); + cy.wrap(response?.statusCode).should('eql', 200); + cy.wrap(response?.body.data.persistTimeline.timeline.kqlMode).should('eql', 'search'); cy.get(ADD_FILTER).should('not.exist'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts index 47e6b0d34e3c5..73eb141f1ce3d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/urls/state.spec.ts @@ -250,8 +250,8 @@ describe('url state', () => { cy.wait('@timeline').then(({ response }) => { closeTimeline(); - cy.wrap(response!.statusCode).should('eql', 200); - const timelineId = response!.body.data.persistTimeline.timeline.savedObjectId; + cy.wrap(response?.statusCode).should('eql', 200); + const timelineId = response?.body.data.persistTimeline.timeline.savedObjectId; cy.visit('/app/home'); cy.visit(`/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t)`); cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).should('exist'); diff --git a/x-pack/plugins/security_solution/cypress/integration/value_lists/value_lists.spec.ts b/x-pack/plugins/security_solution/cypress/integration/value_lists/value_lists.spec.ts index a7cc412a920c0..f2b87e42685e7 100644 --- a/x-pack/plugins/security_solution/cypress/integration/value_lists/value_lists.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/value_lists/value_lists.spec.ts @@ -174,8 +174,8 @@ describe('value lists', () => { cy.wait('@exportList').then(({ response }) => { cy.fixture(listName).then((list: string) => { const [lineOne, lineTwo] = list.split('\n'); - expect(response!.body).to.contain(lineOne); - expect(response!.body).to.contain(lineTwo); + expect(response?.body).to.contain(lineOne); + expect(response?.body).to.contain(lineTwo); }); }); }); @@ -189,8 +189,8 @@ describe('value lists', () => { cy.wait('@exportList').then(({ response }) => { cy.fixture(listName).then((list: string) => { const [lineOne, lineTwo] = list.split('\n'); - expect(response!.body).to.contain(lineOne); - expect(response!.body).to.contain(lineTwo); + expect(response?.body).to.contain(lineOne); + expect(response?.body).to.contain(lineTwo); }); }); }); @@ -204,8 +204,8 @@ describe('value lists', () => { cy.wait('@exportList').then(({ response }) => { cy.fixture(listName).then((list: string) => { const [lineOne, lineTwo] = list.split('\n'); - expect(response!.body).to.contain(lineOne); - expect(response!.body).to.contain(lineTwo); + expect(response?.body).to.contain(lineOne); + expect(response?.body).to.contain(lineTwo); }); }); }); @@ -219,7 +219,7 @@ describe('value lists', () => { cy.wait('@exportList').then(({ response }) => { cy.fixture(listName).then((list: string) => { const [lineOne] = list.split('\n'); - expect(response!.body).to.contain(lineOne); + expect(response?.body).to.contain(lineOne); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/timelines.ts b/x-pack/plugins/security_solution/cypress/screens/timelines.ts index ca60250330f83..92522c44dd8e4 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timelines.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timelines.ts @@ -9,7 +9,10 @@ export const BULK_ACTIONS = '[data-test-subj="utility-bar-action-button"]'; export const EXPORT_TIMELINE_ACTION = '[data-test-subj="export-timeline-action"]'; -export const TIMELINE = (id: string) => { +export const TIMELINE = (id: string | undefined) => { + if (id == null) { + throw new TypeError('id should never be null or undefined'); + } return `[data-test-subj="title-${id}"]`; }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts index 591be21b5682b..b9323fee44d5c 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/create_new_rule.ts @@ -254,7 +254,7 @@ export const fillDefineCustomRuleWithImportedQueryAndContinue = ( rule: CustomRule | OverrideRule ) => { cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id!)).click(); + cy.get(TIMELINE(rule.timeline.id)).click(); cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); cy.get(DEFINE_CONTINUE_BUTTON).should('exist').click({ force: true }); @@ -273,7 +273,7 @@ export const fillDefineThresholdRule = (rule: ThresholdRule) => { const threshold = 1; cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id!)).click(); + cy.get(TIMELINE(rule.timeline.id)).click(); cy.get(COMBO_BOX_CLEAR_BTN).first().click(); rule.index.forEach((index) => { @@ -298,7 +298,7 @@ export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { cy.wrap($el).type(rule.thresholdField, { delay: 35 }); cy.get(IMPORT_QUERY_FROM_SAVED_TIMELINE_LINK).click(); - cy.get(TIMELINE(rule.timeline.id!)).click(); + cy.get(TIMELINE(rule.timeline.id)).click(); cy.get(CUSTOM_QUERY_INPUT).should('have.value', rule.customQuery); cy.get(THRESHOLD_INPUT_AREA) .find(INPUT) @@ -314,9 +314,12 @@ export const fillDefineThresholdRuleAndContinue = (rule: ThresholdRule) => { }; export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => { + if (rule.customQuery == null) { + throw new TypeError('The rule custom query should never be undefined or null '); + } cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).should('exist'); cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).should('be.visible'); - cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).type(rule.customQuery!); + cy.get(RULES_CREATION_FORM).find(EQL_QUERY_INPUT).type(rule.customQuery); cy.get(RULES_CREATION_FORM).find(EQL_QUERY_VALIDATION_SPINNER).should('not.exist'); cy.get(RULES_CREATION_PREVIEW) .find(QUERY_PREVIEW_BUTTON) diff --git a/x-pack/plugins/security_solution/cypress/tasks/login.ts b/x-pack/plugins/security_solution/cypress/tasks/login.ts index 5a935702131d6..96d37d2d9214a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/login.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/login.ts @@ -57,7 +57,7 @@ const LOGIN_API_ENDPOINT = '/internal/security/login'; */ export const getUrlWithRoute = (role: ROLES, route: string) => { const url = Cypress.config().baseUrl; - const kibana = new URL(url!); + const kibana = new URL(String(url)); const theUrl = `${Url.format({ auth: `${role}:changeme`, username: role, @@ -83,7 +83,7 @@ interface User { */ export const constructUrlWithUser = (user: User, route: string) => { const url = Cypress.config().baseUrl; - const kibana = new URL(url!); + const kibana = new URL(String(url)); const hostname = kibana.hostname; const username = user.username; const password = user.password; diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index 37968e700fb39..f001af9df62d2 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -32,7 +32,7 @@ export const activatesRule = () => { cy.get(RULE_SWITCH).should('be.visible'); cy.get(RULE_SWITCH).click(); cy.wait('@bulk_update').then(({ response }) => { - cy.wrap(response!.statusCode).should('eql', 200); + cy.wrap(response?.statusCode).should('eql', 200); }); }; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx b/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx index 527ef2721ab86..20ab5cca89a76 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/barchart.tsx @@ -94,7 +94,7 @@ export const BarChartBaseComponent = ({ yAccessors={yAccessors} timeZone={timeZone} splitSeriesAccessors={splitSeriesAccessors} - data={series.value!} + data={series.value ?? []} stackAccessors={get('configs.series.stackAccessors', chartConfigs)} color={series.color ? series.color : undefined} /> diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.test.tsx index d164d1f8f0ba0..bcdec78fe0614 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.test.tsx @@ -12,7 +12,7 @@ import { mockBrowserFields } from '../../containers/source/mock'; const aField = { ...mockDetailItemData[4], - ...mockBrowserFields.base.fields!['@timestamp'], + ...mockBrowserFields.base.fields?.['@timestamp'], }; describe('helpers', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx index a1c74a603fbc3..47d0ccf5ba3b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/helpers.tsx @@ -127,7 +127,7 @@ export const getColumnsWithTimestamp = ({ export const getExampleText = (example: string | number | null | undefined): string => !isEmpty(example) ? `Example: ${example}` : ''; -export const getIconFromType = (type: string | null) => { +export const getIconFromType = (type: string | null | undefined) => { switch (type) { case 'string': // fall through case 'keyword': diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 632197aa6b219..fd644d1380ddb 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -236,7 +236,7 @@ const EventsViewerComponent: React.FC = ({ useTimelineEvents({ docValueFields, fields, - filterQuery: combinedQueries!.filterQuery, + filterQuery: combinedQueries?.filterQuery, id, indexNames, limit: itemsPerPage, @@ -300,7 +300,7 @@ const EventsViewerComponent: React.FC = ({ height={headerFilterGroup ? COMPACT_HEADER_HEIGHT : EVENTS_VIEWER_HEADER_HEIGHT} subtitle={utilityBar ? undefined : subtitle} title={globalFullScreen ? titleWithExitFullScreen : justTitle} - isInspectDisabled={combinedQueries!.filterQuery === undefined} + isInspectDisabled={combinedQueries?.filterQuery === undefined} > {HeaderSectionContent} diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index d60db8a4bc461..1e61e69180f91 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -181,7 +181,7 @@ const StatefulEventsViewerComponent: React.FC = ({ browserFields, bulkActions, columns, - dataProviders: dataProviders!, + dataProviders, defaultCellActions, deletedEventIds, docValueFields, @@ -199,7 +199,7 @@ const StatefulEventsViewerComponent: React.FC = ({ isLive, isLoadingIndexPattern, itemsPerPage, - itemsPerPageOptions: itemsPerPageOptions!, + itemsPerPageOptions, kqlMode, leadingControlColumns, onRuleChange, @@ -220,7 +220,7 @@ const StatefulEventsViewerComponent: React.FC = ({ columns={columns} docValueFields={docValueFields} id={id} - dataProviders={dataProviders!} + dataProviders={dataProviders} deletedEventIds={deletedEventIds} end={end} isLoadingIndexPattern={isLoadingIndexPattern} @@ -228,8 +228,8 @@ const StatefulEventsViewerComponent: React.FC = ({ indexNames={selectedPatterns} indexPattern={indexPattern} isLive={isLive} - itemsPerPage={itemsPerPage!} - itemsPerPageOptions={itemsPerPageOptions!} + itemsPerPage={itemsPerPage} + itemsPerPageOptions={itemsPerPageOptions} kqlMode={kqlMode} query={query} onRuleChange={onRuleChange} diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts index f4e3814738f92..7262264d72103 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts @@ -54,6 +54,7 @@ export const useSetBreadcrumbs = () => { dispatch(timelineActions.showTimeline({ id: TimelineId.active, show: false })); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion navigateToUrl(breadcrumb.href!); }, } diff --git a/x-pack/plugins/security_solution/public/common/components/utils.ts b/x-pack/plugins/security_solution/public/common/components/utils.ts index fc27578487ca7..da92c94d3c1cd 100644 --- a/x-pack/plugins/security_solution/public/common/components/utils.ts +++ b/x-pack/plugins/security_solution/public/common/components/utils.ts @@ -22,6 +22,7 @@ export const getDaysDiff = (minDate: moment.Moment, maxDate: moment.Moment) => { }; export const histogramDateTimeFormatter = (domain: [string, string] | null, fixedDiff?: number) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const diff = fixedDiff ?? getDaysDiff(moment(domain![0]), moment(domain![1])); const format = niceTimeFormatByDay(diff); return timeFormatter(format); diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index 20d411a0437c2..ed2a2252bd0d2 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -78,7 +78,7 @@ const experimentalFeaturesReducer: Reducer { ...mockEcsDataWithAlert, signal: { rule: { - ...mockEcsDataWithAlert.signal?.rule!, + ...mockEcsDataWithAlert.signal?.rule, // @ts-expect-error timeline_id: null, }, @@ -317,7 +317,7 @@ describe('alert actions', () => { ...mockEcsDataWithAlert, signal: { rule: { - ...mockEcsDataWithAlert.signal?.rule!, + ...mockEcsDataWithAlert.signal?.rule, timeline_id: [''], }, }, @@ -343,7 +343,7 @@ describe('alert actions', () => { ...mockEcsDataWithAlert, signal: { rule: { - ...mockEcsDataWithAlert.signal?.rule!, + ...mockEcsDataWithAlert.signal?.rule, type: ['eql'], timeline_id: [''], }, @@ -387,7 +387,7 @@ describe('alert actions', () => { ...mockEcsDataWithAlert, signal: { rule: { - ...mockEcsDataWithAlert.signal?.rule!, + ...mockEcsDataWithAlert.signal?.rule, type: ['eql'], timeline_id: [''], }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index d48bc95f5d480..fb958c775e68c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -192,8 +192,10 @@ export const getThresholdAggregationData = ( }; } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const originalTime = moment(thresholdData.signal?.original_time![0]); const now = moment(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const ruleFrom = dateMath.parse(thresholdData.signal?.rule?.from![0]!); const ruleInterval = moment.duration(now.diff(ruleFrom)); const fromOriginalTime = originalTime.clone().subtract(ruleInterval); // This is the default... can overshoot diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index d7c48c4f18bc8..4bd516d0b1338 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -142,14 +142,14 @@ export const AlertsTableComponent: React.FC = ({ const setEventsLoadingCallback = useCallback( ({ eventIds, isLoading }: SetEventsLoadingProps) => { - setEventsLoading!({ id: timelineId, eventIds, isLoading }); + setEventsLoading({ id: timelineId, eventIds, isLoading }); }, [setEventsLoading, timelineId] ); const setEventsDeletedCallback = useCallback( ({ eventIds, isDeleted }: SetEventsDeletedProps) => { - setEventsDeleted!({ id: timelineId, eventIds, isDeleted }); + setEventsDeleted({ id: timelineId, eventIds, isDeleted }); }, [setEventsDeleted, timelineId] ); @@ -216,7 +216,7 @@ export const AlertsTableComponent: React.FC = ({ // Callback for clearing entire selection from utility bar const clearSelectionCallback = useCallback(() => { - clearSelected!({ id: timelineId }); + clearSelected({ id: timelineId }); dispatch( timelineActions.setTGridSelectAll({ id: timelineId, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx index 6d7b5d4acc5b8..f785ec43a8b31 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/ml_job_select/index.tsx @@ -59,6 +59,7 @@ interface MlJobSelectProps { } const renderJobOption = (option: MlJobOption) => ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ); @@ -69,6 +70,7 @@ export const MlJobSelect: React.FC = ({ describedByIds = [], f const mlUrl = useKibana().services.application.getUrlForApp('ml'); const handleJobSelect = useCallback( (selectedJobOptions: MlJobOption[]): void => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const selectedJobIds = selectedJobOptions.map((option) => option.value!.id); field.setValue(selectedJobIds); }, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx index dc20dd1aa9ca4..dd836a04f8263 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_switch/index.tsx @@ -61,9 +61,9 @@ export const RuleSwitchComponent = ({ async (event: EuiSwitchEvent) => { setMyIsLoading(true); if (dispatch != null) { - await enableRulesAction([id], event.target.checked!, dispatch, dispatchToaster); + await enableRulesAction([id], event.target.checked, dispatch, dispatchToaster); } else { - const enabling = event.target.checked!; + const enabling = event.target.checked; const title = enabling ? i18n.BATCH_ACTION_ACTIVATE_SELECTED_ERROR(1) : i18n.BATCH_ACTION_DEACTIVATE_SELECTED_ERROR(1); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 848bdd7f8ef71..ebda4f2b4232f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -176,8 +176,8 @@ const DetectionEnginePageComponent: React.FC = ({ const onFilterGroupChangedCallback = useCallback( (newFilterGroup: Status) => { const timelineId = TimelineId.detectionsPage; - clearEventsLoading!({ id: timelineId }); - clearEventsDeleted!({ id: timelineId }); + clearEventsLoading({ id: timelineId }); + clearEventsDeleted({ id: timelineId }); setFilterGroup(newFilterGroup); }, [clearEventsLoading, clearEventsDeleted, setFilterGroup] diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx index 492b8e461fb60..7167b07c7da5d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.tsx @@ -302,6 +302,7 @@ const RuleDetailsPageComponent: React.FC = ({ const getLegacyUrlConflictCallout = useMemo(() => { const outcome = rule?.outcome; if (rule != null && spacesApi && outcome === 'conflict') { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const aliasTargetId = rule?.alias_target_id!; // This is always defined if outcome === 'conflict' // We have resolved to one rule, but there is another one with a legacy URL associated with this page. Display a // callout with a warning for the user, and provide a way for them to navigate to the other rule. @@ -401,9 +402,9 @@ const RuleDetailsPageComponent: React.FC = ({ const onFilterGroupChangedCallback = useCallback( (newFilterGroup: Status) => { const timelineId = TimelineId.detectionsRulesDetailsPage; - clearEventsLoading!({ id: timelineId }); - clearEventsDeleted!({ id: timelineId }); - clearSelected!({ id: timelineId }); + clearEventsLoading({ id: timelineId }); + clearEventsDeleted({ id: timelineId }); + clearSelected({ id: timelineId }); setFilterGroup(newFilterGroup); }, [clearEventsLoading, clearEventsDeleted, clearSelected, setFilterGroup] diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 0f109214c6bf3..18b5d74516199 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -174,6 +174,7 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu timestampOverride: timestampOverride ?? '', name, description, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion note: note!, references, severity: { diff --git a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx index b83853eec69a1..23e5da28a3559 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.tsx @@ -229,8 +229,8 @@ const getAuthenticationColumns = (): AuthTableColumns => [ truncateText: false, hideForMobile: false, render: ({ node }) => - has('lastSuccess.timestamp', node) && node.lastSuccess!.timestamp != null ? ( - + has('lastSuccess.timestamp', node) && node.lastSuccess?.timestamp != null ? ( + ) : ( getEmptyTagValue() ), @@ -264,8 +264,8 @@ const getAuthenticationColumns = (): AuthTableColumns => [ truncateText: false, hideForMobile: false, render: ({ node }) => - has('lastFailure.timestamp', node) && node.lastFailure!.timestamp != null ? ( - + has('lastFailure.timestamp', node) && node.lastFailure?.timestamp != null ? ( + ) : ( getEmptyTagValue() ), diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_card_grid/artifact_card_grid.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_card_grid/artifact_card_grid.tsx index 9e9082ccc54e7..0218b83288d84 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_card_grid/artifact_card_grid.tsx +++ b/x-pack/plugins/security_solution/public/management/components/artifact_card_grid/artifact_card_grid.tsx @@ -119,6 +119,7 @@ export const ArtifactCardGrid = memo( const handleItemComponentProps = useCallback( (item: AnyArtifact): ArtifactEntryCollapsibleCardProps => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return fullCardProps.get(item)!; }, [fullCardProps] diff --git a/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx index c71eea2aaf9db..47242ed7d1edc 100644 --- a/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx +++ b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx @@ -31,7 +31,7 @@ const EuiButtonEmptyStyled = styled(EuiButtonEmpty)` export type BackToExternalAppButtonProps = CommonProps & ListPageRouteState; export const BackToExternalAppButton = memo( ({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo, ...commonProps }) => { - const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo!); + const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo); return ( ( flush="left" size="xs" iconType="arrowLeft" - href={backButtonUrl!} + href={backButtonUrl} onClick={handleBackOnClick} textProps={{ className: 'text' }} > diff --git a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx index 41abb0309a7d1..6e33ad9218bb6 100644 --- a/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx +++ b/x-pack/plugins/security_solution/public/management/components/context_menu_with_router_support/context_menu_with_router_support.tsx @@ -75,6 +75,7 @@ export const ContextMenuWithRouterSupport = memo { + let appTestContext: AppContextTestRender; + let renderResult: ReturnType; + let render: ( + props?: Partial + ) => ReturnType; + + const loadedUserEndpointPrivilegesState = ( + endpointOverrides: Partial = {} + ): EndpointPrivileges => ({ + loading: false, + canAccessFleet: true, + canAccessEndpointManagement: true, + isPlatinumPlus: false, + ...endpointOverrides, + }); + beforeEach(() => { onSearchMock = jest.fn(); + appTestContext = createAppRootMockRenderer(); + + render = (overrideProps = {}) => { + const props: SearchExceptionsProps = { + placeholder: 'search test', + onSearch: onSearchMock, + ...overrideProps, + }; + + renderResult = appTestContext.render(); + return renderResult; + }; + + mockUseEndpointPrivileges.mockReturnValue(loadedUserEndpointPrivilegesState()); }); - const getElement = (defaultValue: string = '') => ( - - ); + afterAll(() => { + mockUseEndpointPrivileges.mockReset(); + }); it('should have a default value', () => { const expectedDefaultValue = 'this is a default value'; - const element = mount(getElement(expectedDefaultValue)); - const defaultValue = element - .find('[data-test-subj="searchField"]') - .first() - .props().defaultValue; - expect(defaultValue).toBe(expectedDefaultValue); + const element = render({ defaultValue: expectedDefaultValue }); + + expect(element.getByDisplayValue(expectedDefaultValue)).not.toBeNull(); }); it('should dispatch search action when submit search field', () => { const expectedDefaultValue = 'this is a default value'; - const element = mount(getElement()); + const element = render(); expect(onSearchMock).toHaveBeenCalledTimes(0); - const searchFieldProps = element - .find('[data-test-subj="searchField"]') - .first() - .props() as EuiFieldSearchPropsFake; - searchFieldProps.onSearch(expectedDefaultValue); + act(() => { + fireEvent.change(element.getByTestId('searchField'), { + target: { value: expectedDefaultValue }, + }); + }); expect(onSearchMock).toHaveBeenCalledTimes(1); expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, '', ''); @@ -56,11 +83,42 @@ describe('Search exceptions', () => { it('should dispatch search action when click on button', () => { const expectedDefaultValue = 'this is a default value'; - const element = mount(getElement(expectedDefaultValue)); + const element = render({ defaultValue: expectedDefaultValue }); expect(onSearchMock).toHaveBeenCalledTimes(0); - element.find('[data-test-subj="searchButton"]').first().simulate('click'); + act(() => { + fireEvent.click(element.getByTestId('searchButton')); + }); + expect(onSearchMock).toHaveBeenCalledTimes(1); expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, '', ''); }); + + it('should hide refresh button', () => { + const element = render({ hideRefreshButton: true }); + + expect(element.queryByTestId('searchButton')).toBeNull(); + }); + + it('should hide policies selector when no license', () => { + const generator = new EndpointDocGenerator('policy-list'); + const policy = generator.generatePolicyPackagePolicy(); + mockUseEndpointPrivileges.mockReturnValue( + loadedUserEndpointPrivilegesState({ isPlatinumPlus: false }) + ); + const element = render({ policyList: [policy], hasPolicyFilter: true }); + + expect(element.queryByTestId('policiesSelectorButton')).toBeNull(); + }); + + it('should display policies selector when right license', () => { + const generator = new EndpointDocGenerator('policy-list'); + const policy = generator.generatePolicyPackagePolicy(); + mockUseEndpointPrivileges.mockReturnValue( + loadedUserEndpointPrivilegesState({ isPlatinumPlus: true }) + ); + const element = render({ policyList: [policy], hasPolicyFilter: true }); + + expect(element.queryByTestId('policiesSelectorButton')).not.toBeNull(); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx index 2b7b2e6b66884..1f3eab5db2947 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx @@ -19,6 +19,7 @@ export interface SearchExceptionsProps { policyList?: ImmutableArray; defaultExcludedPolicies?: string; defaultIncludedPolicies?: string; + hideRefreshButton?: boolean; onSearch(query: string, includedPolicies?: string, excludedPolicies?: string): void; } @@ -31,6 +32,7 @@ export const SearchExceptions = memo( policyList, defaultIncludedPolicies, defaultExcludedPolicies, + hideRefreshButton = false, }) => { const { isPlatinumPlus } = useEndpointPrivileges(); const [query, setQuery] = useState(defaultValue); @@ -101,13 +103,15 @@ export const SearchExceptions = memo( ) : null} - - - {i18n.translate('xpack.securitySolution.management.search.button', { - defaultMessage: 'Refresh', - })} - - + {!hideRefreshButton ? ( + + + {i18n.translate('xpack.securitySolution.management.search.button', { + defaultMessage: 'Refresh', + })} + + + ) : null} ); } diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index b16caf00b4e28..d9407e310639e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -50,9 +50,9 @@ const handleEndpointDetailsActivityLogChanged: CaseReducer = (state, action) => { return { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...state!, isolationRequestState: action.payload, }; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.test.tsx index ed18c084c2a05..108e3d06affa6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/event_filter_delete_modal.test.tsx @@ -30,12 +30,12 @@ describe('When event filters delete modal is shown', () => { const getConfirmButton = () => renderResult.baseElement.querySelector( '[data-test-subj="eventFilterDeleteModalConfirmButton"]' - )! as HTMLButtonElement; + ) as HTMLButtonElement; const getCancelButton = () => renderResult.baseElement.querySelector( '[data-test-subj="eventFilterDeleteModalCancelButton"]' - )! as HTMLButtonElement; + ) as HTMLButtonElement; const getCurrentState = () => store.getState().management.eventFilters; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/flyout/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/flyout/index.test.tsx index d5a1c6624923b..83b4f005135ba 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/flyout/index.test.tsx @@ -80,7 +80,7 @@ describe('Event filter flyout', () => { }); expect(getFormEntryState(getState())).not.toBeUndefined(); - expect(getFormEntryState(getState())!.entries[0].field).toBe(''); + expect(getFormEntryState(getState())?.entries[0].field).toBe(''); }); it('should confirm form when button is disabled', () => { @@ -98,7 +98,7 @@ describe('Event filter flyout', () => { type: 'eventFiltersChangeForm', payload: { entry: { - ...(getState().form!.entry as CreateExceptionListItemSchema), + ...(getState().form?.entry as CreateExceptionListItemSchema), name: 'test', os_types: ['windows'], }, @@ -125,7 +125,7 @@ describe('Event filter flyout', () => { type: 'eventFiltersFormStateChanged', payload: { type: 'LoadedResourceState', - data: getState().form!.entry as ExceptionListItemSchema, + data: getState().form?.entry as ExceptionListItemSchema, }, }); }); @@ -193,6 +193,6 @@ describe('Event filter flyout', () => { }); expect(getFormEntryState(getState())).not.toBeUndefined(); - expect(getFormEntryState(getState())!.item_id).toBe(createdEventFilterEntryMock().item_id); + expect(getFormEntryState(getState())?.item_id).toBe(createdEventFilterEntryMock().item_id); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx index 3934e3a389c36..f8dd9ac632cd0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form/index.test.tsx @@ -99,7 +99,7 @@ describe('Event filter form', () => { }); }); - expect(getState().form.entry!.name).toBe('Exception name'); + expect(getState().form.entry?.name).toBe('Exception name'); expect(getState().form.hasNameError).toBeFalsy(); }); @@ -116,7 +116,7 @@ describe('Event filter form', () => { }); }); - expect(getState().form.entry!.name).toBe(''); + expect(getState().form.entry?.name).toBe(''); expect(getState().form.hasNameError).toBeTruthy(); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/modal/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/modal/index.test.tsx index c77188694f507..8ea50ecd460e4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/modal/index.test.tsx @@ -81,7 +81,7 @@ describe('Event filter modal', () => { await waitForAction('eventFiltersInitForm'); }); - expect(getState().form!.entry).not.toBeUndefined(); + expect(getState().form?.entry).not.toBeUndefined(); }); it('should set OS with the enriched data', async () => { @@ -90,7 +90,7 @@ describe('Event filter modal', () => { await waitForAction('eventFiltersInitForm'); }); - expect(getState().form!.entry?.os_types).toContain('linux'); + expect(getState().form?.entry?.os_types).toContain('linux'); }); it('should confirm form when button is disabled', async () => { @@ -103,7 +103,7 @@ describe('Event filter modal', () => { act(() => { fireEvent.click(confirmButton); }); - expect(getState().form!.submissionResourceState.type).toBe('UninitialisedResourceState'); + expect(getState().form?.submissionResourceState.type).toBe('UninitialisedResourceState'); }); it('should confirm form when button is enabled', async () => { @@ -116,7 +116,7 @@ describe('Event filter modal', () => { type: 'eventFiltersChangeForm', payload: { entry: { - ...(getState().form!.entry as CreateExceptionListItemSchema), + ...(getState().form?.entry as CreateExceptionListItemSchema), name: 'test', }, hasNameError: false, @@ -126,7 +126,7 @@ describe('Event filter modal', () => { act(() => { fireEvent.click(confirmButton); }); - expect(getState().form!.submissionResourceState.type).toBe('LoadingResourceState'); + expect(getState().form?.submissionResourceState.type).toBe('LoadingResourceState'); expect(confirmButton.hasAttribute('disabled')).toBeTruthy(); }); @@ -143,7 +143,7 @@ describe('Event filter modal', () => { type: 'eventFiltersFormStateChanged', payload: { type: 'LoadedResourceState', - data: getState().form!.entry as ExceptionListItemSchema, + data: getState().form?.entry as ExceptionListItemSchema, }, }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/use_event_filters_notification.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/use_event_filters_notification.test.tsx index 039aeb9f8e596..82fe231505ad5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/use_event_filters_notification.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/use_event_filters_notification.test.tsx @@ -79,14 +79,14 @@ describe('EventFiltersNotification', () => { type: 'eventFiltersFormStateChanged', payload: { type: 'LoadedResourceState', - data: store.getState()!.management!.eventFilters!.form!.entry as ExceptionListItemSchema, + data: store.getState().management.eventFilters.form.entry as ExceptionListItemSchema, }, }); }); expect(notifications.toasts.addSuccess).toBeCalledWith( getCreationSuccessMessage( - store.getState()!.management!.eventFilters!.form!.entry as CreateExceptionListItemSchema + store.getState().management.eventFilters.form.entry as CreateExceptionListItemSchema ) ); expect(notifications.toasts.addDanger).not.toBeCalled(); @@ -110,14 +110,14 @@ describe('EventFiltersNotification', () => { type: 'eventFiltersFormStateChanged', payload: { type: 'LoadedResourceState', - data: store.getState()!.management!.eventFilters!.form!.entry as ExceptionListItemSchema, + data: store.getState().management.eventFilters.form.entry as ExceptionListItemSchema, }, }); }); expect(notifications.toasts.addSuccess).toBeCalledWith( getUpdateSuccessMessage( - store.getState()!.management!.eventFilters!.form!.entry as CreateExceptionListItemSchema + store.getState().management.eventFilters.form.entry as CreateExceptionListItemSchema ) ); expect(notifications.toasts.addDanger).not.toBeCalled(); @@ -144,7 +144,7 @@ describe('EventFiltersNotification', () => { type: 'FailedResourceState', error: { message: 'error message', statusCode: 500, error: 'error' }, lastLoadedState: getLastLoadedResourceState( - store.getState()!.management!.eventFilters!.form!.submissionResourceState + store.getState().management.eventFilters.form.submissionResourceState ), }, }); @@ -154,7 +154,7 @@ describe('EventFiltersNotification', () => { expect(notifications.toasts.addDanger).toBeCalledWith( getCreationErrorMessage( ( - store.getState()!.management!.eventFilters!.form! + store.getState().management.eventFilters.form .submissionResourceState as FailedResourceState ).error ) @@ -181,7 +181,7 @@ describe('EventFiltersNotification', () => { type: 'FailedResourceState', error: { message: 'error message', statusCode: 500, error: 'error' }, lastLoadedState: getLastLoadedResourceState( - store.getState()!.management!.eventFilters!.form!.submissionResourceState + store.getState().management.eventFilters.form.submissionResourceState ), }, }); @@ -191,7 +191,7 @@ describe('EventFiltersNotification', () => { expect(notifications.toasts.addDanger).toBeCalledWith( getUpdateErrorMessage( ( - store.getState()!.management!.eventFilters!.form! + store.getState().management.eventFilters.form .submissionResourceState as FailedResourceState ).error ) @@ -211,7 +211,7 @@ describe('EventFiltersNotification', () => { type: 'FailedResourceState', error: { message: 'error message', statusCode: 500, error: 'error' }, lastLoadedState: getLastLoadedResourceState( - store.getState()!.management!.eventFilters!.form!.submissionResourceState + store.getState().management.eventFilters.form.submissionResourceState ), }, }); @@ -221,7 +221,7 @@ describe('EventFiltersNotification', () => { expect(notifications.toasts.addWarning).toBeCalledWith( getGetErrorMessage( ( - store.getState()!.management!.eventFilters!.form! + store.getState().management.eventFilters.form .submissionResourceState as FailedResourceState ).error ) diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/delete_modal.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/delete_modal.test.tsx index 2a75ab0622128..2118a8de9b9ed 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/delete_modal.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/delete_modal.test.tsx @@ -53,11 +53,11 @@ describe('When on the host isolation exceptions delete modal', () => { const submitButton = renderResult.baseElement.querySelector( '[data-test-subj="hostIsolationExceptionsDeleteModalConfirmButton"]' - )! as HTMLButtonElement; + ) as HTMLButtonElement; const cancelButton = renderResult.baseElement.querySelector( '[data-test-subj="hostIsolationExceptionsDeleteModalConfirmButton"]' - )! as HTMLButtonElement; + ) as HTMLButtonElement; act(() => { fireEvent.click(submitButton); @@ -72,7 +72,7 @@ describe('When on the host isolation exceptions delete modal', () => { render(); const cancelButton = renderResult.baseElement.querySelector( '[data-test-subj="hostIsolationExceptionsDeleteModalConfirmButton"]' - )! as HTMLButtonElement; + ) as HTMLButtonElement; const waiter = waitForAction('hostIsolationExceptionsMarkToDelete', { validate: ({ payload }) => { @@ -96,7 +96,7 @@ describe('When on the host isolation exceptions delete modal', () => { const submitButton = renderResult.baseElement.querySelector( '[data-test-subj="hostIsolationExceptionsDeleteModalConfirmButton"]' - )! as HTMLButtonElement; + ) as HTMLButtonElement; await act(async () => { fireEvent.click(submitButton); @@ -121,7 +121,7 @@ describe('When on the host isolation exceptions delete modal', () => { const submitButton = renderResult.baseElement.querySelector( '[data-test-subj="hostIsolationExceptionsDeleteModalConfirmButton"]' - )! as HTMLButtonElement; + ) as HTMLButtonElement; await act(async () => { fireEvent.click(submitButton); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx index de12616c67a3c..799e327a3fb4c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form_flyout.tsx @@ -97,7 +97,7 @@ export const HostIsolationExceptionsFormFlyout: React.FC<{}> = memo(() => { if (!exceptionToEdit || location.id !== exceptionToEdit.id) { dispatch({ type: 'hostIsolationExceptionsMarkToEdit', - payload: { id: location.id! }, + payload: { id: location.id }, }); } else { setException(exceptionToEdit); diff --git a/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts b/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts index 6f90fcd629485..0a440afcb2c30 100644 --- a/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts @@ -75,6 +75,7 @@ export const trustedAppPutHttpMocks = httpHandlerMockFactory { it('windows process events is enabled', () => { const config = policyConfig(getState()); - expect(config!.windows.events.process).toEqual(true); + expect(config.windows.events.process).toEqual(true); }); }); @@ -128,7 +128,7 @@ describe('policy details: ', () => { it('mac file events is enabled', () => { const config = policyConfig(getState()); - expect(config!.mac.events.file).toEqual(true); + expect(config.mac.events.file).toEqual(true); }); }); @@ -150,7 +150,7 @@ describe('policy details: ', () => { it('linux file events is enabled', () => { const config = policyConfig(getState()); - expect(config!.linux.events.file).toEqual(true); + expect(config.linux.events.file).toEqual(true); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.test.tsx index 1d9edbe66fc78..b348a99d223b8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/exception_items_summary.test.tsx @@ -20,7 +20,7 @@ const mockTheme = getMockTheme({ }); const getStatValue = (el: reactTestingLibrary.RenderResult, stat: string) => { - return el.getByText(stat)!.nextSibling?.lastChild?.textContent; + return el.getByText(stat).nextSibling?.lastChild?.textContent; }; describe('Fleet event filters card', () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx index a4a2ee65c84e7..4573b15b8fabc 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/components/policy_form_layout.tsx @@ -101,6 +101,7 @@ export const PolicyFormLayout = React.memo(() => { title: i18n.translate('xpack.securitySolution.endpoint.policy.details.updateErrorTitle', { defaultMessage: 'Failed!', }), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion text: policyUpdateStatus.error!.message, }); } diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts index c6b89b4137cc4..9e53eb9cfc40f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_hooks.ts @@ -93,7 +93,7 @@ export const usePolicyTrustedAppsNotification = () => { 'xpack.securitySolution.endpoint.policy.trustedApps.layout.flyout.toastSuccess.textSingle', { defaultMessage: '"{name}" has been added to your trusted applications list.', - values: { name: updatedArtifacts[0]!.data.name }, + values: { name: updatedArtifacts[0].data.name }, } ), }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.tsx index 8728104aee637..bbf2f3b208754 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.tsx @@ -91,6 +91,7 @@ export const PolicyTrustedAppsFlyout = React.memo(() => { payload: { action: 'assign', artifacts: selectedArtifactIds.map>((selectedId) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return assignableArtifactsList?.data?.find((trustedApp) => trustedApp.id === selectedId)!; }), }, @@ -182,6 +183,7 @@ export const PolicyTrustedAppsFlyout = React.memo(() => { defaultMessage: 'Search trusted applications', } )} + hideRefreshButton /> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx index cb29d0ff868ac..5d6c9731c7070 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx @@ -181,6 +181,7 @@ export const PolicyTrustedAppsList = memo(() => { const provideCardProps = useCallback['cardComponentProps']>( (item) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return cardProps.get(item as Immutable)!; }, [cardProps] diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx index 0411751685a90..917ffe49c6090 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx @@ -25,7 +25,8 @@ import { import { Immutable } from '../../../../../../../common/endpoint/types'; import { HttpFetchOptionsWithPath } from 'kibana/public'; -describe('When using the RemoveTrustedAppFromPolicyModal component', () => { +// FLAKY https://github.com/elastic/kibana/issues/115100 +describe.skip('When using the RemoveTrustedAppFromPolicyModal component', () => { let appTestContext: AppContextTestRender; let renderResult: ReturnType; let render: (waitForLoadedState?: boolean) => Promise>; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts index 0ff6282f8a018..0de5761ccf074 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts @@ -191,6 +191,7 @@ const submitCreationIfNeeded = async ( if (editMode) { responseTrustedApp = ( await trustedAppsService.updateTrustedApp( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion { id: editItemId(currentState)! }, // TODO: try to remove the cast entry as PostTrustedAppCreateRequest @@ -414,6 +415,7 @@ const fetchEditTrustedAppIfNeeded = async ( payload: { // @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830) type: 'LoadingResourceState', + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion previousState: editItemState(currentState)!, }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.tsx index 50485ccde00ad..d4f456ab8e039 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.tsx @@ -86,7 +86,9 @@ const addResultToValidation = ( }; } const errorMarkup: React.ReactNode = type === 'warnings' ?
{resultValue}
: resultValue; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion validation.result[field]![type].push(errorMarkup); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion validation.result[field]!.isInvalid = true; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/effected_policy_select/effected_policy_select.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/effected_policy_select/effected_policy_select.tsx index e247602060384..07de303c155aa 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/effected_policy_select/effected_policy_select.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/effected_policy_select/effected_policy_select.tsx @@ -143,7 +143,7 @@ export const EffectedPolicySelect = memo( }); }, [isGlobal, onChange] - )!; + ); const handleGlobalButtonChange = useCallback( (selectedId) => { diff --git a/x-pack/plugins/security_solution/public/network/components/details/index.tsx b/x-pack/plugins/security_solution/public/network/components/details/index.tsx index 7658a6a76230c..0b53a4bfb3fe2 100644 --- a/x-pack/plugins/security_solution/public/network/components/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/components/details/index.tsx @@ -71,7 +71,7 @@ export const IpOverview = React.memo( const capabilities = useMlCapabilities(); const userPermissions = hasMlUserPermissions(capabilities); const [darkMode] = useUiSetting$(DEFAULT_DARK_MODE); - const typeData = data[flowTarget]!; + const typeData = data[flowTarget]; const column: DescriptionList[] = [ { title: i18n.LOCATION, diff --git a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx index a811f5c92c37a..fc28067866146 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx @@ -78,7 +78,7 @@ describe('NetworkTopNFlow Table Component', () => { ); - expect(store.getState().network.page.queries!.dns.sort).toEqual({ + expect(store.getState().network.page.queries?.dns.sort).toEqual({ direction: 'desc', field: 'queryCount', }); @@ -87,7 +87,7 @@ describe('NetworkTopNFlow Table Component', () => { wrapper.update(); - expect(store.getState().network.page.queries!.dns.sort).toEqual({ + expect(store.getState().network.page.queries?.dns.sort).toEqual({ direction: 'asc', field: 'dnsName', }); diff --git a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx index f05372c76b36f..2a85b31791f5a 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx @@ -80,7 +80,7 @@ describe('NetworkHttp Table Component', () => { ); - expect(store.getState().network.page.queries!.http.sort).toEqual({ + expect(store.getState().network.page.queries?.http.sort).toEqual({ direction: 'desc', }); @@ -88,7 +88,7 @@ describe('NetworkHttp Table Component', () => { wrapper.update(); - expect(store.getState().network.page.queries!.http.sort).toEqual({ + expect(store.getState().network.page.queries?.http.sort).toEqual({ direction: 'asc', }); expect(wrapper.find('.euiTable thead tr th button').first().find('svg')).toBeTruthy(); diff --git a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx index 8f2c7a098a045..3a1a5efef6b89 100644 --- a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx @@ -77,7 +77,7 @@ describe('Tls Table Component', () => { /> ); - expect(store.getState().network.details.queries!.tls.sort).toEqual({ + expect(store.getState().network.details.queries?.tls.sort).toEqual({ direction: 'desc', field: '_id', }); @@ -86,7 +86,7 @@ describe('Tls Table Component', () => { wrapper.update(); - expect(store.getState().network.details.queries!.tls.sort).toEqual({ + expect(store.getState().network.details.queries?.tls.sort).toEqual({ direction: 'asc', field: '_id', }); diff --git a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx index 69027ad9bd9f8..3861433b4dcb0 100644 --- a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx @@ -81,7 +81,7 @@ describe('Users Table Component', () => { /> ); - expect(store.getState().network.details.queries!.users.sort).toEqual({ + expect(store.getState().network.details.queries?.users.sort).toEqual({ direction: 'asc', field: 'name', }); @@ -90,7 +90,7 @@ describe('Users Table Component', () => { wrapper.update(); - expect(store.getState().network.details.queries!.users.sort).toEqual({ + expect(store.getState().network.details.queries?.users.sort).toEqual({ direction: 'desc', field: 'name', }); diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 718487ccf7fc6..371b68e9bec8e 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -371,16 +371,23 @@ export class Plugin implements IPlugin node.id === originID)! ); const relatedEvents = [ diff --git a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts index 3a8bf76e732a9..6dfeaa9723a33 100644 --- a/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts +++ b/x-pack/plugins/security_solution/public/resolver/models/indexed_process_tree/index.ts @@ -162,6 +162,7 @@ export function root(tree: IndexedProcessTree) { // iteratively swap current w/ its parent while (parent(tree, current) !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion current = parent(tree, current)!; } return current; diff --git a/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts b/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts index 110ea476f5fb2..26cf874edda00 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/selectors.test.ts @@ -145,7 +145,7 @@ describe('resolver selectors', () => { }); }); it('the origin should be in view', () => { - const origin = selectors.graphNodeForID(state())(originID)!; + const origin = selectors.graphNodeForID(state())(originID); expect( selectors .visibleNodesAndEdgeLines(state())(0) @@ -153,7 +153,7 @@ describe('resolver selectors', () => { ).toBe(true); }); it('the first child should be in view', () => { - const firstChild = selectors.graphNodeForID(state())(firstChildID)!; + const firstChild = selectors.graphNodeForID(state())(firstChildID); expect( selectors .visibleNodesAndEdgeLines(state())(0) @@ -161,7 +161,7 @@ describe('resolver selectors', () => { ).toBe(true); }); it('the second child should not be in view', () => { - const secondChild = selectors.graphNodeForID(state())(secondChildID)!; + const secondChild = selectors.graphNodeForID(state())(secondChildID); expect( selectors .visibleNodesAndEdgeLines(state())(0) diff --git a/x-pack/plugins/security_solution/public/resolver/test_utilities/extend_jest.ts b/x-pack/plugins/security_solution/public/resolver/test_utilities/extend_jest.ts index 145e54dbbb7b2..7bb7180712c99 100644 --- a/x-pack/plugins/security_solution/public/resolver/test_utilities/extend_jest.ts +++ b/x-pack/plugins/security_solution/public/resolver/test_utilities/extend_jest.ts @@ -67,7 +67,7 @@ expect.extend({ ? () => `${this.utils.matcherHint(matcherName, undefined, undefined, options)}\n\n` + `Expected: not ${this.utils.printExpected(expected)}\n${ - this.utils.stringify(expected) !== this.utils.stringify(received[received.length - 1]!) + this.utils.stringify(expected) !== this.utils.stringify(received[received.length - 1]) ? `Received: ${this.utils.printReceived(received[received.length - 1])}` : '' }` @@ -131,7 +131,7 @@ expect.extend({ ? () => `${this.utils.matcherHint(matcherName, undefined, undefined, options)}\n\n` + `Expected: not ${this.utils.printExpected(expected)}\n${ - this.utils.stringify(expected) !== this.utils.stringify(received[received.length - 1]!) + this.utils.stringify(expected) !== this.utils.stringify(received[received.length - 1]) ? `Received: ${this.utils.printReceived(received[received.length - 1])}` : '' }` diff --git a/x-pack/plugins/security_solution/public/resolver/view/graph_controls.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/graph_controls.test.tsx index 1a7477af3b3cf..6a146882fbab5 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/graph_controls.test.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/graph_controls.test.tsx @@ -104,7 +104,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the user clicks the west panning button', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:west-button'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:west-button'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); }); @@ -118,7 +118,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the user clicks the south panning button', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:south-button'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:south-button'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); }); @@ -132,7 +132,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the user clicks the east panning button', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:east-button'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:east-button'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); }); @@ -146,7 +146,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the user clicks the north panning button', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:north-button'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:north-button'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); }); @@ -160,9 +160,9 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the user clicks the center panning button', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:north-button'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:north-button'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); - (await simulator.resolve('resolver:graph-controls:center-button'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:center-button'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); }); @@ -177,7 +177,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the zoom in button is clicked', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:zoom-in'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:zoom-in'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); }); @@ -191,7 +191,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the zoom out button is clicked', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:zoom-out'))!.simulate('click'); + (await simulator.resolve('resolver:graph-controls:zoom-out'))?.simulate('click'); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); }); @@ -207,7 +207,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { beforeEach(async () => { await expect(originNodeStyle()).toYieldObjectEqualTo(originalSizeStyle); - (await simulator.resolve('resolver:graph-controls:zoom-slider'))!.simulate('change', { + (await simulator.resolve('resolver:graph-controls:zoom-slider'))?.simulate('change', { target: { value: 0.8 }, }); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); @@ -223,7 +223,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the slider is moved downwards', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:zoom-slider'))!.simulate('change', { + (await simulator.resolve('resolver:graph-controls:zoom-slider'))?.simulate('change', { target: { value: 0.2 }, }); simulator.runAnimationFramesTimeFromNow(nudgeAnimationDuration); @@ -239,7 +239,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the schema information button is clicked', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:schema-info-button'))!.simulate('click', { + (await simulator.resolve('resolver:graph-controls:schema-info-button'))?.simulate('click', { button: 0, }); }); @@ -257,7 +257,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the node legend button is clicked', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:node-legend-button'))!.simulate('click', { + (await simulator.resolve('resolver:graph-controls:node-legend-button'))?.simulate('click', { button: 0, }); }); @@ -275,7 +275,7 @@ describe('graph controls: when relsover is loaded with an origin node', () => { describe('when the node legend button is clicked while the schema info button is open', () => { beforeEach(async () => { - (await simulator.resolve('resolver:graph-controls:schema-info-button'))!.simulate('click', { + (await simulator.resolve('resolver:graph-controls:schema-info-button'))?.simulate('click', { button: 0, }); }); @@ -284,8 +284,8 @@ describe('graph controls: when relsover is loaded with an origin node', () => { expect(simulator.testSubject('resolver:graph-controls:schema-info').length).toBe(1); await simulator - .testSubject('resolver:graph-controls:node-legend-button')! - .simulate('click', { button: 0 }); + .testSubject('resolver:graph-controls:node-legend-button') + ?.simulate('click', { button: 0 }); await expect( simulator.map(() => ({ diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx index 3b2d222ac3812..72db334e17c2c 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx @@ -150,13 +150,13 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and .filterWhere(Simulator.isDOM); expect(copyableFieldHoverArea).toHaveLength(1); - copyableFieldHoverArea!.simulate('mouseenter'); + copyableFieldHoverArea?.simulate('mouseenter'); }); describe('and when they click the copy-to-clipboard button', () => { beforeEach(async () => { const copyButton = await simulator().resolve('resolver:panel:clipboard'); expect(copyButton).toHaveLength(1); - copyButton!.simulate('click'); + copyButton?.simulate('click'); simulator().confirmTextWrittenToClipboard(); }); it(`should write ${value} to the clipboard`, async () => { @@ -232,11 +232,11 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and .filterWhere(Simulator.isDOM) ); }); - cExtHoverArea!.simulate('mouseenter'); + cExtHoverArea?.simulate('mouseenter'); }); describe('and when the user clicks the copy-to-clipboard button', () => { beforeEach(async () => { - (await simulator().resolve('resolver:panel:clipboard'))!.simulate('click'); + (await simulator().resolve('resolver:panel:clipboard'))?.simulate('click'); simulator().confirmTextWrittenToClipboard(); }); const expected = 'Sep 23, 2020 @ 08:25:32.316'; @@ -369,7 +369,7 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and beforeEach(async () => { const button = await simulator().resolve('resolver:panel:clipboard'); expect(button).toBeTruthy(); - button!.simulate('click'); + button?.simulate('click'); simulator().confirmTextWrittenToClipboard(); }); it(`should write ${expectedValue} to the clipboard`, async () => { diff --git a/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts b/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts index 8c3caf16eadd7..b3289a510deef 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts +++ b/x-pack/plugins/security_solution/public/resolver/view/side_effect_simulator_factory.ts @@ -41,6 +41,7 @@ export const sideEffectSimulatorFactory: () => SideEffectSimulator = () => { */ const getBoundingClientRect: (target: Element) => DOMRect = (target) => { if (contentRects.has(target)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return contentRects.get(target)!; } const domRect: DOMRect = { diff --git a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.test.tsx index 9851f11d9adba..e76e2800908c2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/field_renderers.test.tsx @@ -70,7 +70,7 @@ describe('Field Renderers', () => { describe('#dateRenderer', () => { test('it renders correctly against snapshot', () => { - const wrapper = shallow(dateRenderer(mockData.complete.source!.firstSeen)); + const wrapper = shallow(dateRenderer(mockData.complete.source?.firstSeen)); expect(wrapper).toMatchSnapshot(); }); @@ -307,7 +307,7 @@ describe('Field Renderers', () => { ); expect( - wrapper.find('[data-test-subj="more-container"]').first().props().style!.overflow + wrapper.find('[data-test-subj="more-container"]').first().props().style?.overflow ).toEqual('auto'); }); @@ -322,7 +322,7 @@ describe('Field Renderers', () => { ); expect( - wrapper.find('[data-test-subj="more-container"]').first().props().style!.maxHeight + wrapper.find('[data-test-subj="more-container"]').first().props().style?.maxHeight ).toEqual(DEFAULT_MORE_MAX_HEIGHT); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx index 4eb91ca8ee272..639bc1ac6b57f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx @@ -77,6 +77,7 @@ const ActiveTimelinesComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index 9f1730c367a81..1fdfb744f3071 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -113,6 +113,7 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline [dataProviders, kqlQuery] ); const getKqlQueryTimeline = useMemo(() => timelineSelectors.getKqlFilterQuerySelector(), []); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const kqlQueryTimeline = useSelector((state: State) => getKqlQueryTimeline(state, timelineId)!); const kqlQueryExpression = @@ -333,6 +334,7 @@ const TimelineStatusInfoComponent: React.FC = ({ timelineId } @@ -372,6 +374,7 @@ const FlyoutHeaderComponent: React.FC = ({ timelineId }) => { ); const { dataProviders, filters, timelineType, kqlMode, activeTab } = timeline; const getKqlQueryTimeline = useMemo(() => timelineSelectors.getKqlFilterQuerySelector(), []); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const kqlQueryTimeline = useSelector((state: State) => getKqlQueryTimeline(state, timelineId)!); const kqlQueryExpression = diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 2a3b49517b456..1fbddf61f8cd3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -205,9 +205,11 @@ const convertToDefaultField = ({ and, ...dataProvider }: DataProviderResult) => if (dataProvider.type === DataProviderType.template) { return deepMerge(dataProvider, { type: DataProviderType.default, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion enabled: dataProvider.queryMatch!.operator !== IS_OPERATOR, queryMatch: { value: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion dataProvider.queryMatch!.operator === IS_OPERATOR ? '' : dataProvider.queryMatch!.value, }, }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx index d59b75fcfb506..f3ec55ea0ddef 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.test.tsx @@ -318,7 +318,7 @@ describe('StatefulOpenTimeline', () => { await waitFor(() => { expect( wrapper.find(`.${OPEN_TIMELINE_CLASS_NAME} input`).first().getDOMNode().id === - document.activeElement!.id + document.activeElement?.id ).toBe(true); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx index 1cca5a3999b81..607bccdbc039d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.test.tsx @@ -44,7 +44,7 @@ describe('NotePreviews', () => { const wrapper = mountWithIntl(); - hasNotes[0].notes!.forEach(({ savedObjectId }) => { + hasNotes[0].notes?.forEach(({ savedObjectId }) => { expect(wrapper.find(`[data-test-subj="note-preview-${savedObjectId}"]`).exists()).toBe(true); }); }); @@ -54,7 +54,7 @@ describe('NotePreviews', () => { const wrapper = mountWithIntl(); - hasNotes[0].notes!.forEach(({ savedObjectId }) => { + hasNotes[0].notes?.forEach(({ savedObjectId }) => { expect(wrapper.find(`[data-test-subj="note-preview-${savedObjectId}"]`).exists()).toBe(true); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx index b3cd5eedd19c3..a7953d60ba767 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.tsx @@ -40,6 +40,7 @@ export const getActionsColumns = ({ onOpenTimeline({ duplicate: true, timelineType: TimelineType.default, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion timelineId: savedObjectId!, }); }, @@ -58,6 +59,7 @@ export const getActionsColumns = ({ onOpenTimeline({ duplicate: true, timelineType: TimelineType.template, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion timelineId: savedObjectId!, }); }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx index 53382fe8fa21d..17d43d80a5a9a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx @@ -108,7 +108,7 @@ export const ExpandableEvent = React.memo( ( {':'} - + {header.type}

diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx index a7c989bfab64e..1473de5472b3e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx @@ -187,6 +187,7 @@ const StatefulEventComponent: React.FC = ({ const handleOnEventDetailPanelOpened = useCallback(() => { const eventId = event._id; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const indexName = event._index!; const updatedExpandedDetail: TimelineExpandedDetailType = { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx index fc8bf2086471c..3ee0ef8804e89 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx @@ -111,7 +111,7 @@ export const BodyComponent = React.memo( const onRowSelected: OnRowSelected = useCallback( ({ eventIds, isSelected }: { eventIds: string[]; isSelected: boolean }) => { - setSelected!({ + setSelected({ id, eventIds: getEventIdToDataMapping(data, eventIds, queryFields), isSelected, @@ -125,7 +125,7 @@ export const BodyComponent = React.memo( const onSelectAll: OnSelectAll = useCallback( ({ isSelected }: { isSelected: boolean }) => isSelected - ? setSelected!({ + ? setSelected({ id, eventIds: getEventIdToDataMapping( data, @@ -135,7 +135,7 @@ export const BodyComponent = React.memo( isSelected, isSelectAllChecked: isSelected, }) - : clearSelected!({ id }), + : clearSelected({ id }), [setSelected, clearSelected, id, data, queryFields] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx index 3703d6fe441c0..61ea659964e4d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx @@ -78,7 +78,7 @@ describe('suricata_row_renderer', () => { }); test('should render a suricata row even if it does not have a suricata signature', () => { - delete suricata!.suricata!.eve!.alert!.signature; + delete suricata?.suricata?.eve?.alert?.signature; const children = suricataRowRenderer.renderRow({ browserFields: mockBrowserFields, data: suricata, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx index 41f35e7c50e30..7c010795682ac 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx @@ -76,12 +76,12 @@ export const DraggableZeekElement = React.memo<{ and: [], enabled: true, id: escapeDataProviderId(`draggable-zeek-element-draggable-wrapper-${id}-${field}-${value}`), - name: value!, + name: String(value), excluded: false, kqlQuery: '', queryMatch: { field, - value: value!, + value: String(value), operator: IS_OPERATOR as QueryOperator, }, }), @@ -97,7 +97,7 @@ export const DraggableZeekElement = React.memo<{ ) : ( - {stringRenderer(value!)} + {stringRenderer(String(value))} ), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx index 84f286b435a48..52443cf92a9cb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx @@ -113,7 +113,7 @@ const AddDataProviderPopoverComponent: React.FC = ( width: 400, content: ( = ( width: 400, content: ( = ( return ( = ({ users }) => { const List = useMemo( () => users.map((user) => ( - - + + )), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index d2cf158818f75..2082e7f5b69bb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -463,7 +463,7 @@ const makeMapStateToProps = () => { status, timelineType, } = timeline; - const kqlQueryTimeline = getKqlQueryTimeline(state, timelineId)!; + const kqlQueryTimeline = getKqlQueryTimeline(state, timelineId); const timelineFilter = kqlMode === 'filter' ? filters || [] : []; // return events on empty search diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx index 33ab2e0049828..96ca26a099d2f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx @@ -74,7 +74,7 @@ const StatefulSearchOrFilterComponent = React.memo( from={from} fromStr={fromStr} isRefreshPaused={isRefreshPaused} - kqlMode={kqlMode!} + kqlMode={kqlMode} refreshInterval={refreshInterval} savedQueryId={savedQueryId} setFilters={setFiltersInTimeline} @@ -82,7 +82,7 @@ const StatefulSearchOrFilterComponent = React.memo( timelineId={timelineId} to={to} toStr={toStr} - updateKqlMode={updateKqlMode!} + updateKqlMode={updateKqlMode} updateReduxTime={updateReduxTime} /> ); @@ -119,15 +119,19 @@ const makeMapStateToProps = () => { const policy: inputsModel.Policy = getInputsPolicy(state); return { dataProviders: timeline.dataProviders, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion filterQuery: getKqlFilterQuery(state, timelineId)!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion filters: timeline.filters!, from: input.timerange.from, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion fromStr: input.timerange.fromStr!, isRefreshPaused: policy.kind === 'manual', kqlMode: getOr('filter', 'kqlMode', timeline), refreshInterval: policy.duration, savedQueryId: getOr(null, 'savedQueryId', timeline), to: input.timerange.to, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion toStr: input.timerange.toStr!, }; }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx index 44174009d0198..7269c005e9af5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.test.tsx @@ -47,7 +47,7 @@ describe('SelectableTimeline', () => { const searchProps: EuiSelectableProps['searchProps'] = wrapper .find('[data-test-subj="selectable-input"]') .prop('searchProps'); - expect(searchProps!.placeholder).toEqual('e.g. Timeline name or description'); + expect(searchProps?.placeholder).toEqual('e.g. Timeline name or description'); }); }); @@ -65,7 +65,7 @@ describe('SelectableTimeline', () => { const searchProps: EuiSelectableProps['searchProps'] = wrapper .find('[data-test-subj="selectable-input"]') .prop('searchProps'); - expect(searchProps!.placeholder).toEqual('e.g. Timeline template name or description'); + expect(searchProps?.placeholder).toEqual('e.g. Timeline template name or description'); }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx index 9d10584130194..e4070a051af46 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx @@ -110,8 +110,8 @@ const SelectableTimelineComponent: React.FC = ({ selectableListOuterRef.current && selectableListInnerRef.current ) { - const clientHeight = selectableListOuterRef.current!.clientHeight; - const scrollHeight = selectableListInnerRef.current!.clientHeight; + const clientHeight = selectableListOuterRef.current.clientHeight; + const scrollHeight = selectableListInnerRef.current.clientHeight; const clientHeightTrigger = clientHeight * 1.2; if ( scrollOffset > 10 && diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx index 3514766b334a0..af05198ef9974 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/styles.tsx @@ -271,13 +271,13 @@ export const EventsTrData = styled.div.attrs(({ className = '' }) => ({ const TIMELINE_EVENT_DETAILS_OFFSET = 40; interface WidthProp { - width?: number; + width: number; } export const EventsTrSupplementContainer = styled.div.attrs(({ width }) => ({ role: 'dialog', style: { - width: `${width! - TIMELINE_EVENT_DETAILS_OFFSET}px`, + width: `${width - TIMELINE_EVENT_DETAILS_OFFSET}px`, }, }))``; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/api.ts b/x-pack/plugins/security_solution/public/timelines/containers/api.ts index 789c942d0e29a..7f74912be09b4 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/api.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/api.ts @@ -156,6 +156,7 @@ export const persistTimeline = async ({ try { if (isEmpty(timelineId) && timeline.status === TimelineStatus.draft && timeline) { const temp: TimelineResponse | TimelineErrorResponse = await cleanDraftTimeline({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion timelineType: timeline.timelineType!, templateTimelineId: timeline.templateTimelineId ?? undefined, templateTimelineVersion: timeline.templateTimelineVersion ?? undefined, @@ -163,7 +164,7 @@ export const persistTimeline = async ({ const draftTimeline = decodeTimelineResponse(temp); const templateTimelineInfo = - timeline.timelineType! === TimelineType.template + timeline.timelineType === TimelineType.template ? { templateTimelineId: draftTimeline.data.persistTimeline.timeline.templateTimelineId ?? diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts index 9d95036cb6076..164f293edee65 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts @@ -235,7 +235,7 @@ export const createTimelineEpic = mergeMap(([result, recentTimeline, allTimelineQuery, kibana]) => { const error = result as TimelineErrorResponse; if (error.status_code != null && error.status_code === 405) { - kibana.notifications!.toasts.addDanger({ + kibana.notifications.toasts.addDanger({ title: i18n.UPDATE_TIMELINE_ERROR_TITLE, text: error.message ?? i18n.UPDATE_TIMELINE_ERROR_TEXT, }); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts index eceafb9b56cdd..639d1b1e4f489 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts @@ -1126,8 +1126,8 @@ describe('Timeline', () => { const newAndProvider = update.foo.dataProviders[indexProvider].and.find( (i) => i.id === '456' ); - expect(oldAndProvider!.enabled).toEqual(false); - expect(newAndProvider!.enabled).toEqual(true); + expect(oldAndProvider?.enabled).toEqual(false); + expect(newAndProvider?.enabled).toEqual(true); }); }); @@ -1386,8 +1386,8 @@ describe('Timeline', () => { const newAndProvider = update.foo.dataProviders[indexProvider].and.find( (i) => i.id === '456' ); - expect(oldAndProvider!.excluded).toEqual(true); - expect(newAndProvider!.excluded).toEqual(false); + expect(oldAndProvider?.excluded).toEqual(true); + expect(newAndProvider?.excluded).toEqual(false); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts index a116311becfe5..cf6665a5dde2b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/task.ts @@ -135,6 +135,7 @@ export class ManifestTask { } } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (oldManifest! == null) { this.logger.debug('Last computed manifest not available yet'); return; diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts index a1b93b5bd59f7..32d5806a4b47a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts @@ -130,7 +130,7 @@ describe('Policy-Changing license watcher', () => { expect(packagePolicySvcMock.update).toHaveBeenCalled(); expect( - packagePolicySvcMock.update.mock.calls[0][3].inputs[0].config!.policy.value.windows.popup + packagePolicySvcMock.update.mock.calls[0][3].inputs[0].config?.policy.value.windows.popup .malware.message ).not.toEqual(CustomMessage); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts index 4652630649ffc..e12299bedbb34 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts @@ -189,6 +189,7 @@ export const isolationRequestHandler = function ( }, } as Omit, user: { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion id: user!.username, }, }; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts index 39aa0bf2d8cf7..9b454a266834c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/enrichment.test.ts @@ -114,8 +114,8 @@ describe('test document enrichment', () => { const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); expect(enrichedHostList.policy_info).toBeDefined(); - expect(enrichedHostList.policy_info!.agent.applied.id).toEqual(policyID); - expect(enrichedHostList.policy_info!.agent.applied.revision).toEqual(policyRev); + expect(enrichedHostList.policy_info?.agent.applied.id).toEqual(policyID); + expect(enrichedHostList.policy_info?.agent.applied.revision).toEqual(policyRev); }); it('reflects current fleet agent info', async () => { @@ -130,8 +130,8 @@ describe('test document enrichment', () => { const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); expect(enrichedHostList.policy_info).toBeDefined(); - expect(enrichedHostList.policy_info!.agent.configured.id).toEqual(policyID); - expect(enrichedHostList.policy_info!.agent.configured.revision).toEqual(policyRev); + expect(enrichedHostList.policy_info?.agent.configured.id).toEqual(policyID); + expect(enrichedHostList.policy_info?.agent.configured.revision).toEqual(policyRev); }); it('reflects current endpoint policy info', async () => { @@ -151,8 +151,8 @@ describe('test document enrichment', () => { const enrichedHostList = await enrichHostMetadata(docGen.generateHostMetadata(), metaReqCtx); expect(enrichedHostList.policy_info).toBeDefined(); - expect(enrichedHostList.policy_info!.endpoint.id).toEqual(policyID); - expect(enrichedHostList.policy_info!.endpoint.revision).toEqual(policyRev); + expect(enrichedHostList.policy_info?.endpoint.id).toEqual(policyID); + expect(enrichedHostList.policy_info?.endpoint.revision).toEqual(policyRev); }); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts index 5c06dbd3db14c..027107bcf1a59 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts @@ -114,6 +114,7 @@ export const getMetadataListRequestHandler = function ( } const endpointPolicies = await getAllEndpointPackagePolicies( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion endpointAppContext.service.getPackagePolicyService()!, context.core.savedObjects.client ); @@ -344,6 +345,7 @@ export async function enrichHostMetadata( const status = await metadataRequestContext.endpointAppContextService ?.getAgentService() ?.getAgentStatusById(esClient.asCurrentUser, elasticAgentId); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion hostStatus = fleetAgentStatusToEndpointHostStatus(status!); } catch (e) { if (e instanceof AgentNotFoundError) { @@ -361,6 +363,7 @@ export async function enrichHostMetadata( ?.getAgent(esClient.asCurrentUser, elasticAgentId); const agentPolicy = await metadataRequestContext.endpointAppContextService .getAgentPolicyService() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ?.get(esSavedObjectClient, agent?.policy_id!, true); const endpointPolicy = ((agentPolicy?.package_policies || []) as PackagePolicy[]).find( (policy: PackagePolicy) => policy.package?.name === 'endpoint' @@ -404,6 +407,7 @@ async function legacyListMetadataQuery( logger: Logger, endpointPolicies: PackagePolicy[] ): Promise { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const agentService = endpointAppContext.service.getAgentService()!; const metadataRequestContext: MetadataRequestContext = { @@ -512,11 +516,15 @@ async function queryUnitedIndex( return metadata && agent; }) .map((doc) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { endpoint: metadata, agent } = doc!._source!.united!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const agentPolicy = agentPoliciesMap[agent.policy_id!]; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const endpointPolicy = endpointPoliciesMap[agent.policy_id!]; return { metadata, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion host_status: fleetAgentStatusToEndpointHostStatus(agent.last_checkin_status!), policy_info: { agent: { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index d9016e7a9c7cb..3e5050c05814a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -171,8 +171,8 @@ describe('test endpoint route', () => { const esSearchMock = mockScopedClient.asCurrentUser.search; // should be called twice, united index first, then legacy index expect(esSearchMock).toHaveBeenCalledTimes(2); - expect(esSearchMock.mock.calls[0][0]!.index).toEqual(METADATA_UNITED_INDEX); - expect(esSearchMock.mock.calls[1][0]!.index).toEqual(metadataCurrentIndexPattern); + expect(esSearchMock.mock.calls[0][0]?.index).toEqual(METADATA_UNITED_INDEX); + expect(esSearchMock.mock.calls[1][0]?.index).toEqual(metadataCurrentIndexPattern); expect(routeConfig.options).toEqual({ authRequired: true, tags: ['access:securitySolution'], @@ -224,7 +224,7 @@ describe('test endpoint route', () => { ); expect(esSearchMock).toHaveBeenCalledTimes(1); - expect(esSearchMock.mock.calls[0][0]!.index).toEqual(METADATA_UNITED_INDEX); + expect(esSearchMock.mock.calls[0][0]?.index).toEqual(METADATA_UNITED_INDEX); expect(esSearchMock.mock.calls[0][0]?.body?.query).toEqual({ bool: { must: [ diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts index 448496671b4f9..7b09013496c6d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/query_builders.ts @@ -53,8 +53,8 @@ export async function kibanaRequestToMetadataListESQuery( body: { query: buildQueryBody( request, - queryBuilderOptions?.unenrolledAgentIds!, - queryBuilderOptions?.statusAgentIds! + queryBuilderOptions?.unenrolledAgentIds, + queryBuilderOptions?.statusAgentIds ), track_total_hits: true, sort: MetadataSortMethod, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts b/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts index 987bef15afe98..ce46395a1f09f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts @@ -120,12 +120,14 @@ export async function agentVersionsMap( const result: Map = new Map(); let hasMore = true; while (hasMore) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const queryResult = await endpointAppContext.service .getAgentService()! .listAgents(esClient, searchOptions(page++)); queryResult.agents.forEach((agent: Agent) => { const agentVersion = agent.local_metadata?.elastic?.agent?.version; if (result.has(agentVersion)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion result.set(agentVersion, result.get(agentVersion)! + 1); } else { result.set(agentVersion, 1); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts index 9cefc55eddec4..856a615c1ffa2 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts @@ -142,6 +142,7 @@ export const getTrustedAppsList = async ( data: results?.data.map(exceptionListItemToTrustedApp) ?? [], total: results?.total ?? 0, page: results?.page ?? 1, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion per_page: results?.per_page ?? perPage!, }; }; @@ -262,6 +263,7 @@ export const getTrustedAppsSummary = async ( let page = 1; while (paging) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { data, total } = (await exceptionsListClient.findExceptionListItem({ listId: ENDPOINT_TRUSTED_APPS_LIST_ID, page, diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts index a04a6eea5ab65..711d78ba51b59 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts @@ -200,6 +200,7 @@ export const getPendingActionCounts = async ( }, { ignore: [404] } ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion .then((result) => result.body?.hits?.hits?.map((a) => a._source!) || []) .catch(catchAndWrapError); @@ -272,6 +273,7 @@ const fetchActionResponseIds = async ( }, { ignore: [404] } ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion .then((result) => result.body?.hits?.hits?.map((a) => a._source!) || []) .catch(catchAndWrapError); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts index ac19757e037b2..062702bb14a49 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/artifact_client.ts @@ -37,6 +37,7 @@ export class EndpointArtifactClient implements EndpointArtifactClientInterface { return { type: idPieces[1], + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion decodedSha256: idPieces.pop()!, identifier: idPieces.join('-'), }; @@ -74,7 +75,7 @@ export class EndpointArtifactClient implements EndpointArtifactClientInterface { async deleteArtifact(id: string) { // Ignoring the `id` not being in the type until we can refactor the types in endpoint. - // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const artifactId = (await this.getArtifact(id))?.id!; return this.fleetArtifacts.deleteArtifact(artifactId); } diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts index 87a73e0130113..a7fb5dc23877c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.mock.ts @@ -42,14 +42,14 @@ export const mockFindExceptionListItemResponses = ( responses: Record> ) => { return jest.fn().mockImplementation((options: FindExceptionListItemOptions) => { - const matches = options.filter!.match(FILTER_REGEXP) || []; + const matches = options.filter?.match(FILTER_REGEXP) || []; - if (matches[4] && responses[options.listId]?.[`${matches![1]}-${matches[4]}`]) { + if (matches[4] && responses[options.listId]?.[`${matches?.[1]}-${matches[4]}`]) { return createExceptionListResponse( - responses[options.listId]?.[`${matches![1]}-${matches[4]}`] || [] + responses[options.listId]?.[`${matches?.[1]}-${matches[4]}`] || [] ); } else { - return createExceptionListResponse(responses[options.listId]?.[matches![1] || ''] || []); + return createExceptionListResponse(responses[options.listId]?.[matches?.[1] || ''] || []); } }); }; diff --git a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts index 5e2f46aa4c285..23c21e431a344 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/metadata/endpoint_metadata_service.ts @@ -171,6 +171,7 @@ export class EndpointMetadataService { // Get Agent Policy and Endpoint Package Policy if (fleetAgent) { try { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion fleetAgentPolicy = await this.getFleetAgentPolicy(fleetAgent.policy_id!); endpointPackagePolicy = fleetAgentPolicy.package_policies.find( (policy) => policy.package?.name === 'endpoint' @@ -183,7 +184,8 @@ export class EndpointMetadataService { return { metadata: endpointMetadata, host_status: fleetAgent - ? fleetAgentStatusToEndpointHostStatus(fleetAgent.status!) + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + fleetAgentStatusToEndpointHostStatus(fleetAgent.status!) : DEFAULT_ENDPOINT_HOST_STATUS, policy_info: { agent: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts index f9693c87631b7..8914e8eec87d0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts @@ -94,7 +94,7 @@ export const createMigration = async ({ return { destinationIndex: migrationIndex, sourceIndex: index, - taskId: String(response.body.task!), + taskId: String(response.body.task), version, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index 61635fdcef9f0..6ec23c32e4976 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -58,7 +58,7 @@ export const createIndexRoute = ( if (!siemClient) { return siemResponse.error({ statusCode: 404 }); } - await createDetectionIndex(context, siemClient!, ruleDataService, ruleRegistryEnabled); + await createDetectionIndex(context, siemClient, ruleDataService, ruleRegistryEnabled); return response.ok({ body: { acknowledged: true } }); } catch (err) { const error = transformError(err); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index 48efcc4495aff..26e09d69d3a45 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -124,7 +124,7 @@ describe.each([ }); test('returns 404 if rulesClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const request = addPrepackagedRulesRequest(); const response = await server.inject(request, context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index 2c8696dbd4554..6f721bb2bb9c5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -55,7 +55,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getReadBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index d1be96a44930a..59fe5c0ff68a1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -56,7 +56,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getCreateRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts index 7db5651de2c34..49580fc09ca63 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts @@ -84,7 +84,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getDeleteBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index 7c447660acb45..466012a045eb3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -65,7 +65,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getDeleteRequest(), context); expect(response.status).toEqual(404); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts index 7ae3f56b6fea9..0b0650d48872f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.test.ts @@ -48,7 +48,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getFindRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts index 053e0b7178de5..5d6b9810a2cda 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.test.ts @@ -41,7 +41,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(ruleStatusRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts index 78572863f7472..e97744a5fe5a8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.test.ts @@ -100,7 +100,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getPrepackagedRulesStatusRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index bf29dbe870153..aa301bcc0335e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -77,7 +77,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(request, context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts index 2c3db023dccc4..d0d5937eab2d7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts @@ -89,7 +89,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getPatchBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts index 97773c45ce0d9..00d7180dfc9be 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.test.ts @@ -69,7 +69,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getPatchRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts index ebc86acc964e6..41b909bd718c0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts @@ -65,7 +65,7 @@ describe.each([ }); it('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getBulkActionRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts index 37b8228ac1e9b..bc9fa43b56ae7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.test.ts @@ -86,7 +86,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getReadRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index 746a40dfa8dc2..f7bef76944a97 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -65,7 +65,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getUpdateBulkRequest(), context); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 5b3e2737418c2..7d611f3cccbf2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -66,7 +66,7 @@ describe.each([ }); test('returns 404 if alertClient is not available on the route', async () => { - context.alerting!.getRulesClient = jest.fn(); + context.alerting.getRulesClient = jest.fn(); const response = await server.inject(getUpdateRequest(), context); expect(response.status).toEqual(404); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts index 279a824426cec..e2be04fc6e7df 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -51,7 +51,7 @@ export const querySignalsRoute = (router: SecuritySolutionPluginRouter, config: }); } const esClient = context.core.elasticsearch.client.asCurrentUser; - const siemClient = context.securitySolution!.getAppClient(); + const siemClient = context.securitySolution.getAppClient(); // TODO: Once we are past experimental phase this code should be removed const { ruleRegistryEnabled } = parseExperimentalConfigValue(config.enableExperimental); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts index d22ca0d1f5090..0dd2acfb88ffe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_event_type_signal.ts @@ -8,8 +8,8 @@ import { BaseSignalHit, SimpleHit } from './types'; import { getField } from './utils'; export const buildEventTypeSignal = (doc: BaseSignalHit): object => { - if (doc._source?.event != null && doc._source?.event instanceof Object) { - return { ...doc._source!.event, kind: 'signal' }; + if (doc._source != null && doc._source.event instanceof Object) { + return { ...doc._source.event, kind: 'signal' }; } else { return { kind: 'signal' }; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts index bd5444a325128..012977da2a00f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts @@ -108,7 +108,7 @@ describe('buildRuleWithOverrides', () => { test('it applies rule name override in buildRule', () => { const ruleSO = sampleRuleSO(getQueryRuleParams()); ruleSO.attributes.params.ruleNameOverride = 'someKey'; - const rule = buildRuleWithOverrides(ruleSO, sampleDocNoSortId()._source!); + const rule = buildRuleWithOverrides(ruleSO, sampleDocNoSortId()._source); const expected = { ...expectedRule(), name: 'someValue', @@ -135,7 +135,7 @@ describe('buildRuleWithOverrides', () => { ]; const doc = sampleDocNoSortId(); doc._source.new_risk_score = newRiskScore; - const rule = buildRuleWithOverrides(ruleSO, doc._source!); + const rule = buildRuleWithOverrides(ruleSO, doc._source); const expected = { ...expectedRule(), risk_score: newRiskScore, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts index 9da5934489023..272c3f64fb105 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/enrich_signal_threat_matches.ts @@ -107,7 +107,7 @@ export const enrichSignalThreatMatches = async ( return { ...signalHit, _source: { - ...signalHit._source!, + ...signalHit._source, threat: { ...threat, enrichments: [...existingEnrichments, ...enrichments[i]], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts index 31bf7674b4f92..c202065176ff1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts @@ -116,6 +116,7 @@ const getTransformedHits = ( ? [ { field: threshold.cardinality[0].field, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion value: bucket.cardinality_count!.value, }, ] @@ -131,6 +132,7 @@ const getTransformedHits = ( }; return getCombinations( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (results.aggregations![aggParts.name] as { buckets: TermAggregationBucket[] }).buckets, 0, aggParts.field diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts index 5cafff24c544b..610be59deaa5f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts @@ -40,6 +40,7 @@ export const getThresholdBucketFilters = async ({ // Terms to filter events older than `lastSignalTimestamp`. bucket.terms.forEach((term) => { if (term.field != null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (filter.bool!.filter as ESFilter[]).push({ term: { [term.field]: `${term.value}`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index 7d2eafa46d382..0e50db97d1256 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -1277,7 +1277,7 @@ describe('utils', () => { test('It returns timestampOverride date time if set', () => { const override = '2020-10-07T19:20:28.049Z'; const searchResult = sampleDocSearchResultsNoSortId(); - searchResult.hits.hits[0]._source!.different_timestamp = new Date(override).toISOString(); + searchResult.hits.hits[0]._source.different_timestamp = new Date(override).toISOString(); const date = lastValidDate({ searchResult, timestampOverride: 'different_timestamp' }); expect(date?.toISOString()).toEqual(override); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 2aefc7ea0bd64..79b36cf62573a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -874,6 +874,7 @@ export const mergeSearchResults = (searchResults: SignalSearchResponse[]) => { aggregations: newAggregations, hits: { total: calculateTotal(prev.hits.total, next.hits.total), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion max_score: Math.max(newHits.max_score!, existingHits.max_score!), hits: [...existingHits.hits, ...newHits.hits], }, diff --git a/x-pack/plugins/security_solution/server/lib/timeline/utils/check_timelines_status.ts b/x-pack/plugins/security_solution/server/lib/timeline/utils/check_timelines_status.ts index 560df1112ac58..f524d0c7ca3a6 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/utils/check_timelines_status.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/utils/check_timelines_status.ts @@ -40,6 +40,7 @@ export const getTimelinesToUpdate = ( installedTimelines.some((installedTimeline) => { return ( timeline.templateTimelineId === installedTimeline.templateTimelineId && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion timeline.templateTimelineVersion! > installedTimeline.templateTimelineVersion! ); }) diff --git a/x-pack/plugins/security_solution/server/lib/timeline/utils/failure_cases.ts b/x-pack/plugins/security_solution/server/lib/timeline/utils/failure_cases.ts index 99365a55a1d61..44f16c57ad2b4 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/utils/failure_cases.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/utils/failure_cases.ts @@ -227,7 +227,7 @@ export const checkIsUpdateViaImportFailureCases = ( return { body: UPDAT_TIMELINE_VIA_IMPORT_NOT_ALLOWED_ERROR_MESSAGE, statusCode: 405 }; } else { return { - body: getImportExistingTimelineError(existTimeline!.savedObjectId), + body: getImportExistingTimelineError(existTimeline.savedObjectId), statusCode: 405, }; } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 7ae4eea4c4700..a6ccc0f5569fb 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -361,6 +361,7 @@ export class Plugin implements IPlugin { ignoreUnavailable: true, index: ['filebeat-*'], }); - const parsedInspect = JSON.parse(parsedResponse.inspect!.dsl[0]); + const parsedInspect = JSON.parse(parsedResponse.inspect.dsl[0]); expect(parsedInspect).toEqual(expectedInspect); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts index c96e0040fd23d..ae68d81d6b922 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts @@ -223,6 +223,7 @@ export const getHostEndpoint = async ( endpointPolicy: endpointData.Endpoint.policy.applied.name, policyStatus: endpointData.Endpoint.policy.applied.status, sensorVersion: endpointData.agent.version, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion elasticAgentStatus: fleetAgentStatusToEndpointHostStatus(fleetAgentStatus!), isolation: endpointData.Endpoint.state?.isolation ?? false, pendingActions, diff --git a/x-pack/plugins/spaces/server/config.ts b/x-pack/plugins/spaces/server/config.ts index 4abaf4f0ca569..a8c3a1c6223da 100644 --- a/x-pack/plugins/spaces/server/config.ts +++ b/x-pack/plugins/spaces/server/config.ts @@ -28,6 +28,7 @@ export function createConfig$(context: PluginInitializerContext) { const disabledDeprecation: ConfigDeprecation = (config, fromPath, addDeprecation) => { if ('enabled' in (config?.xpack?.spaces || {})) { addDeprecation({ + configPath: 'xpack.spaces.enabled', title: i18n.translate('xpack.spaces.deprecations.enabledTitle', { defaultMessage: 'Setting "xpack.spaces.enabled" is deprecated', }), diff --git a/x-pack/plugins/stack_alerts/server/index.ts b/x-pack/plugins/stack_alerts/server/index.ts index 9491f3e646c70..1ac774a2d6c3f 100644 --- a/x-pack/plugins/stack_alerts/server/index.ts +++ b/x-pack/plugins/stack_alerts/server/index.ts @@ -18,6 +18,7 @@ export const config: PluginConfigDescriptor = { const stackAlerts = get(settings, fromPath); if (stackAlerts?.enabled === false || stackAlerts?.enabled === true) { addDeprecation({ + configPath: 'xpack.stack_alerts.enabled', message: `"xpack.stack_alerts.enabled" is deprecated. The ability to disable this plugin will be removed in 8.0.0.`, correctiveActions: { manualSteps: [`Remove "xpack.stack_alerts.enabled" from your kibana configs.`], diff --git a/x-pack/plugins/task_manager/server/index.ts b/x-pack/plugins/task_manager/server/index.ts index 84bee044c4de9..2a360fc1a1d90 100644 --- a/x-pack/plugins/task_manager/server/index.ts +++ b/x-pack/plugins/task_manager/server/index.ts @@ -49,6 +49,7 @@ export const config: PluginConfigDescriptor = { const taskManager = get(settings, fromPath); if (taskManager?.index) { addDeprecation({ + configPath: `${fromPath}.index`, documentationUrl: 'https://ela.st/kbn-remove-legacy-multitenancy', message: `"${fromPath}.index" is deprecated. Multitenancy by changing "kibana.index" will not be supported starting in 8.0. See https://ela.st/kbn-remove-legacy-multitenancy for more details`, correctiveActions: { @@ -61,6 +62,7 @@ export const config: PluginConfigDescriptor = { } if (taskManager?.max_workers > MAX_WORKERS_LIMIT) { addDeprecation({ + configPath: `${fromPath}.max_workers`, message: `setting "${fromPath}.max_workers" (${taskManager?.max_workers}) greater than ${MAX_WORKERS_LIMIT} is deprecated. Values greater than ${MAX_WORKERS_LIMIT} will not be supported starting in 8.0.`, correctiveActions: { manualSteps: [ @@ -75,6 +77,7 @@ export const config: PluginConfigDescriptor = { const taskManager = get(settings, fromPath); if (taskManager?.enabled === false || taskManager?.enabled === true) { addDeprecation({ + configPath: 'xpack.task_manager.enabled', message: `"xpack.task_manager.enabled" is deprecated. The ability to disable this plugin will be removed in 8.0.0.`, correctiveActions: { manualSteps: [`Remove "xpack.task_manager.enabled" from your kibana configs.`], diff --git a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx index af19a6b7cdb74..30181a96aa70b 100644 --- a/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx +++ b/x-pack/plugins/timelines/public/components/actions/timeline/cases/add_to_existing_case_button.tsx @@ -32,7 +32,7 @@ const AddToCaseActionComponent: React.FC = ({ {userCanCrud && ( = ({ {userCanCrud && ( { }; export const getHoverActions = (store?: Store): HoverActionsConfig => ({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion getAddToTimelineButton: getAddToTimelineButtonLazy.bind(null, store!), getColumnToggleButton: getColumnToggleButtonLazy, getCopyButton: getCopyButtonLazy, diff --git a/x-pack/plugins/timelines/public/components/index.tsx b/x-pack/plugins/timelines/public/components/index.tsx index 9959574464836..0ff8c9bf97e8b 100644 --- a/x-pack/plugins/timelines/public/components/index.tsx +++ b/x-pack/plugins/timelines/public/components/index.tsx @@ -41,6 +41,7 @@ export const TGrid = (props: TGridComponent) => { browserFields = (tGridProps as TGridIntegratedProps).browserFields; } return ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/header_tooltip_content/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/header_tooltip_content/index.tsx index b973d99584d61..91dd64d8fed3a 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/header_tooltip_content/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/body/column_headers/header_tooltip_content/index.tsx @@ -61,7 +61,7 @@ export const HeaderToolTipContent = React.memo<{ header: ColumnHeaderOptions }>( {':'} - + {header.type}

diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_event.tsx b/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_event.tsx index f448ba3831b14..d746368e389c8 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_event.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/body/events/stateful_event.tsx @@ -122,6 +122,7 @@ const StatefulEventComponent: React.FC = ({ const handleOnEventDetailPanelOpened = useCallback(() => { const eventId = event._id; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const indexName = event._index!; const updatedExpandedDetail: TimelineExpandedDetailType = { diff --git a/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx b/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx index 02c3d10a76058..e2eb1d4d04547 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/helpers.tsx @@ -192,6 +192,7 @@ export const buildCombinedQuery = (combineQueriesParams: CombineQueries) => { const combinedQuery = combineQueries(combineQueriesParams); return combinedQuery ? { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion filterQuery: replaceStatusField(combinedQuery!.filterQuery), } : null; diff --git a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx index b649dc36abe0a..e363297d04be5 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx @@ -237,7 +237,7 @@ const TGridIntegratedComponent: React.FC = ({ endDate: end, entityType, fields, - filterQuery: combinedQueries!.filterQuery, + filterQuery: combinedQueries?.filterQuery, id, indexNames, limit: itemsPerPage, diff --git a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx index 1a374d0c6b87a..ae092d8634bb7 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx @@ -217,7 +217,7 @@ const TGridStandaloneComponent: React.FC = ({ entityType, excludeEcsData: true, fields, - filterQuery: combinedQueries!.filterQuery, + filterQuery: combinedQueries?.filterQuery, id: STANDALONE_ID, indexNames, limit: itemsPerPageStore, diff --git a/x-pack/plugins/timelines/public/components/t_grid/styles.tsx b/x-pack/plugins/timelines/public/components/t_grid/styles.tsx index 28e425f53824b..9f957e2c61910 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/styles.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/styles.tsx @@ -270,13 +270,13 @@ export const EventsTrData = styled.div.attrs(({ className = '' }) => ({ const TIMELINE_EVENT_DETAILS_OFFSET = 40; interface WidthProp { - width?: number; + width: number; } export const EventsTrSupplementContainer = styled.div.attrs(({ width }) => ({ role: 'dialog', style: { - width: `${width! - TIMELINE_EVENT_DETAILS_OFFSET}px`, + width: `${width - TIMELINE_EVENT_DETAILS_OFFSET}px`, }, }))``; diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx index e44ee1d45d19b..e19499628e8c1 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/field_browser.test.tsx @@ -235,7 +235,7 @@ describe('FieldsBrowser', () => { expect( wrapper.find('[data-test-subj="field-search"]').first().getDOMNode().id === - document.activeElement!.id + document.activeElement?.id ).toBe(true); }); @@ -266,7 +266,7 @@ describe('FieldsBrowser', () => { const changeEvent: any = { target: { value: inputText } }; const onChange = searchField.props().onChange; - onChange!(changeEvent); + onChange?.(changeEvent); searchField.simulate('change').update(); expect(onSearchInputChange).toBeCalledWith(inputText); diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx index f4b608b456fed..552c228363e49 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/helpers.tsx @@ -97,6 +97,7 @@ export const filterBrowserFieldsByFieldName = ({ fields: filter( (f) => f.name != null && f.name.includes(trimmedSubstring), browserFields[categoryId].fields + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ).reduce((filtered, field) => ({ ...filtered, [field.name!]: field }), {}), }, }), diff --git a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx index bdec76418dc8c..0b67f53cca76e 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/toolbar/fields_browser/index.tsx @@ -100,7 +100,9 @@ export const StatefulFieldsBrowserComponent: React.FC = ({ (selected, category) => newFilteredBrowserFields[category].fields != null && newFilteredBrowserFields[selected].fields != null && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion Object.keys(newFilteredBrowserFields[category].fields!).length > + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion Object.keys(newFilteredBrowserFields[selected].fields!).length ? category : selected, diff --git a/x-pack/plugins/timelines/public/components/utils/helpers.ts b/x-pack/plugins/timelines/public/components/utils/helpers.ts index f02c86aecc108..23f92c119d574 100644 --- a/x-pack/plugins/timelines/public/components/utils/helpers.ts +++ b/x-pack/plugins/timelines/public/components/utils/helpers.ts @@ -53,7 +53,7 @@ export const getColumnsWithTimestamp = ({ : []; }; -export const getIconFromType = (type: string | null) => { +export const getIconFromType = (type: string | null | undefined) => { switch (type) { case 'string': // fall through case 'keyword': diff --git a/x-pack/plugins/timelines/public/container/use_update_alerts.ts b/x-pack/plugins/timelines/public/container/use_update_alerts.ts index b38c3b9a71fef..1b9e6218eecca 100644 --- a/x-pack/plugins/timelines/public/container/use_update_alerts.ts +++ b/x-pack/plugins/timelines/public/container/use_update_alerts.ts @@ -17,7 +17,7 @@ import { /** * Update alert status by query - * + * * @param useDetectionEngine logic flag for using the regular Detection Engine URL or the RAC URL * * @param status to update to('open' / 'closed' / 'acknowledged') @@ -40,7 +40,7 @@ export const useUpdateAlertsStatus = ( return { updateAlertStatus: async ({ status, index, query }) => { if (useDetectionEngine) { - return http!.fetch(DETECTION_ENGINE_SIGNALS_STATUS_URL, { + return http.fetch(DETECTION_ENGINE_SIGNALS_STATUS_URL, { method: 'POST', body: JSON.stringify({ status, query }), }); diff --git a/x-pack/plugins/timelines/public/plugin.ts b/x-pack/plugins/timelines/public/plugin.ts index 4b383ce392147..acb7b26d0cf84 100644 --- a/x-pack/plugins/timelines/public/plugin.ts +++ b/x-pack/plugins/timelines/public/plugin.ts @@ -45,7 +45,7 @@ export class TimelinesPlugin implements Plugin { } return { getHoverActions: () => { - return getHoverActions(this._store!); + return getHoverActions(this._store); }, getTGrid: (props: TGridProps) => { if (props.type === 'standalone' && this._store) { @@ -73,6 +73,7 @@ export class TimelinesPlugin implements Plugin { }, getFieldBrowser: (props: FieldBrowserProps) => { return getFieldsBrowserLazy(props, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion store: this._store!, }); }, @@ -90,6 +91,7 @@ export class TimelinesPlugin implements Plugin { }, getAddToCaseAction: (props) => { return getAddToCaseLazy(props, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion store: this._store!, storage: this._storage, setStore: this.setStore.bind(this), @@ -97,6 +99,7 @@ export class TimelinesPlugin implements Plugin { }, getAddToCasePopover: (props) => { return getAddToCasePopoverLazy(props, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion store: this._store!, storage: this._storage, setStore: this.setStore.bind(this), @@ -104,6 +107,7 @@ export class TimelinesPlugin implements Plugin { }, getAddToExistingCaseButton: (props) => { return getAddToExistingCaseButtonLazy(props, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion store: this._store!, storage: this._storage, setStore: this.setStore.bind(this), @@ -111,6 +115,7 @@ export class TimelinesPlugin implements Plugin { }, getAddToNewCaseButton: (props) => { return getAddToNewCaseButtonLazy(props, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion store: this._store!, storage: this._storage, setStore: this.setStore.bind(this), diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 118b99b7cb8ef..a563079a101c3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10843,7 +10843,6 @@ "xpack.fleet.epm.detailsTitle": "詳細", "xpack.fleet.epm.errorLoadingNotice": "NOTICE.txtの読み込みエラー", "xpack.fleet.epm.featuresLabel": "機能", - "xpack.fleet.epm.illustrationAltText": "統合の例", "xpack.fleet.epm.install.packageInstallError": "{pkgName} {pkgVersion}のインストールエラー", "xpack.fleet.epm.install.packageUpdateError": "{pkgName} {pkgVersion}の更新エラー", "xpack.fleet.epm.licenseLabel": "ライセンス", @@ -10878,7 +10877,6 @@ "xpack.fleet.epm.usedByLabel": "エージェントポリシー", "xpack.fleet.epm.versionLabel": "バージョン", "xpack.fleet.epmList.allPackagesFilterLinkText": "すべて", - "xpack.fleet.epmList.allTitle": "カテゴリで参照", "xpack.fleet.epmList.installedTitle": "インストールされている統合", "xpack.fleet.epmList.missingIntegrationPlaceholder": "検索用語と一致する統合が見つかりませんでした。別のキーワードを試すか、左側のカテゴリを使用して参照してください。", "xpack.fleet.epmList.noPackagesFoundPlaceholder": "パッケージが見つかりません", @@ -10952,12 +10950,10 @@ "xpack.fleet.homeIntegration.tutorialModule.noticeText.notePrefix": "注:", "xpack.fleet.hostsInput.addRow": "行の追加", "xpack.fleet.initializationErrorMessageTitle": "Fleet を初期化できません", - "xpack.fleet.integrations.beatsModulesLink": "Beatsモジュール", "xpack.fleet.integrations.customInputsLink": "カスタム入力", "xpack.fleet.integrations.discussForumLink": "ディスカッションフォーラム", "xpack.fleet.integrations.installPackage.installingPackageButtonLabel": "{title} アセットをインストールしています", "xpack.fleet.integrations.installPackage.installPackageButtonLabel": "{title}アセットをインストール", - "xpack.fleet.integrations.missing": "統合が表示されない場合{customInputsLink}を使用してログまたはメトリックを収集するか、{beatsTutorialLink}を使用してデータを追加してください。{discussForumLink}を使用して新しい統合を要求してください。", "xpack.fleet.integrations.packageInstallErrorDescription": "このパッケージのインストール中に問題が発生しました。しばらくたってから再試行してください。", "xpack.fleet.integrations.packageInstallErrorTitle": "{title}パッケージをインストールできませんでした", "xpack.fleet.integrations.packageInstallSuccessDescription": "正常に{title}をインストールしました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c9924169a2eaa..4781e206cac20 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10958,7 +10958,6 @@ "xpack.fleet.epm.detailsTitle": "详情", "xpack.fleet.epm.errorLoadingNotice": "加载 NOTICE.txt 时出错", "xpack.fleet.epm.featuresLabel": "功能", - "xpack.fleet.epm.illustrationAltText": "集成的图示", "xpack.fleet.epm.install.packageInstallError": "安装 {pkgName} {pkgVersion} 时出错", "xpack.fleet.epm.install.packageUpdateError": "将 {pkgName} 更新到 {pkgVersion} 时出错", "xpack.fleet.epm.licenseLabel": "许可证", @@ -10993,7 +10992,6 @@ "xpack.fleet.epm.usedByLabel": "代理策略", "xpack.fleet.epm.versionLabel": "版本", "xpack.fleet.epmList.allPackagesFilterLinkText": "全部", - "xpack.fleet.epmList.allTitle": "按类别浏览", "xpack.fleet.epmList.installedTitle": "已安装集成", "xpack.fleet.epmList.missingIntegrationPlaceholder": "我们未找到任何匹配搜索词的集成。请重试其他关键字,或使用左侧的类别浏览。", "xpack.fleet.epmList.noPackagesFoundPlaceholder": "未找到任何软件包", @@ -11067,12 +11065,10 @@ "xpack.fleet.homeIntegration.tutorialModule.noticeText.notePrefix": "注意:", "xpack.fleet.hostsInput.addRow": "添加行", "xpack.fleet.initializationErrorMessageTitle": "无法初始化 Fleet", - "xpack.fleet.integrations.beatsModulesLink": "Beats 模板", "xpack.fleet.integrations.customInputsLink": "定制输入", "xpack.fleet.integrations.discussForumLink": "讨论论坛", "xpack.fleet.integrations.installPackage.installingPackageButtonLabel": "正在安装 {title} 资产", "xpack.fleet.integrations.installPackage.installPackageButtonLabel": "安装 {title} 资产", - "xpack.fleet.integrations.missing": "未看到集成?使用我们的{customInputsLink}收集任何日志或指标或使用 {beatsTutorialLink} 添加数据。使用{discussForumLink}请求新的集成。", "xpack.fleet.integrations.packageInstallErrorDescription": "尝试安装此软件包时出现问题。请稍后重试。", "xpack.fleet.integrations.packageInstallErrorTitle": "无法安装 {title} 软件包", "xpack.fleet.integrations.packageInstallSuccessDescription": "已成功安装 {title}", diff --git a/x-pack/plugins/triggers_actions_ui/server/index.ts b/x-pack/plugins/triggers_actions_ui/server/index.ts index c7d363af45247..72ca584250d03 100644 --- a/x-pack/plugins/triggers_actions_ui/server/index.ts +++ b/x-pack/plugins/triggers_actions_ui/server/index.ts @@ -31,6 +31,7 @@ export const config: PluginConfigDescriptor = { const triggersActionsUi = get(settings, fromPath); if (triggersActionsUi?.enabled === false || triggersActionsUi?.enabled === true) { addDeprecation({ + configPath: 'xpack.trigger_actions_ui.enabled', message: `"xpack.trigger_actions_ui.enabled" is deprecated. The ability to disable this plugin will be removed in 8.0.0.`, correctiveActions: { manualSteps: [`Remove "xpack.trigger_actions_ui.enabled" from your kibana configs.`], diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/service.mock.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/service.mock.ts index 1767143fdf527..6a3d376acecab 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/service.mock.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/kibana_deprecations/service.mock.ts @@ -22,6 +22,7 @@ const kibanaDeprecations: DomainDeprecationDetails[] = [ title: 'Test deprecation title 1', message: 'Test deprecation message 1', deprecationType: 'config', + configPath: 'test', }, { correctiveActions: { diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx index 23697b00923c8..013f59a7dcf56 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx @@ -75,10 +75,10 @@ export interface DeprecationResolutionState { resolveDeprecationError?: string; } -export interface KibanaDeprecationDetails extends DomainDeprecationDetails { +export type KibanaDeprecationDetails = DomainDeprecationDetails & { id: string; filterType: DomainDeprecationDetails['deprecationType'] | 'uncategorized'; -} +}; const getDeprecationCountByLevel = (deprecations: KibanaDeprecationDetails[]) => { const criticalDeprecations: KibanaDeprecationDetails[] = []; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx index b061ab5ea2d4d..590bfac96770d 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/overview/fix_issues_step/fix_issues_step.tsx @@ -70,7 +70,7 @@ export const getFixIssuesStep = ({

diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/shared/deprecation_count.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/shared/deprecation_count.tsx index 3312508a87073..32d214f0d80f2 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/shared/deprecation_count.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/shared/deprecation_count.tsx @@ -6,10 +6,11 @@ */ import React, { FunctionComponent } from 'react'; - import { EuiFlexGroup, EuiFlexItem, EuiHealth } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { LevelInfoTip } from './level_info_tip'; + const i18nTexts = { getCriticalStatusLabel: (count: number) => i18n.translate('xpack.upgradeAssistant.deprecationCount.criticalStatusLabel', { @@ -39,14 +40,31 @@ export const DeprecationCount: FunctionComponent = ({ return ( - - {i18nTexts.getCriticalStatusLabel(totalCriticalDeprecations)} - + + + + {i18nTexts.getCriticalStatusLabel(totalCriticalDeprecations)} + + + + + + + + - - {i18nTexts.getWarningStatusLabel(totalWarningDeprecations)} - + + + + {i18nTexts.getWarningStatusLabel(totalWarningDeprecations)} + + + + + + + ); diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/shared/index.ts b/x-pack/plugins/upgrade_assistant/public/application/components/shared/index.ts index 57793cbfb2dd6..34496e1e8eb55 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/shared/index.ts +++ b/x-pack/plugins/upgrade_assistant/public/application/components/shared/index.ts @@ -10,3 +10,4 @@ export { DeprecationCount } from './deprecation_count'; export { DeprecationBadge } from './deprecation_badge'; export { DeprecationsPageLoadingError } from './deprecations_page_loading_error'; export { DeprecationFlyoutLearnMoreLink } from './deprecation_flyout_learn_more_link'; +export { LevelInfoTip } from './level_info_tip'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/shared/level_info_tip.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/shared/level_info_tip.tsx new file mode 100644 index 0000000000000..d3600a7290b4e --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/public/application/components/shared/level_info_tip.tsx @@ -0,0 +1,27 @@ +/* + * 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, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiIconTip } from '@elastic/eui'; + +const i18nTexts = { + critical: i18n.translate('xpack.upgradeAssistant.levelInfoTip.criticalLabel', { + defaultMessage: 'Critical issues must be resolved before you upgrade', + }), + warning: i18n.translate('xpack.upgradeAssistant.levelInfoTip.warningLabel', { + defaultMessage: 'Warning issues can be ignored at your discretion', + }), +}; + +interface Props { + level: 'critical' | 'warning'; +} + +export const LevelInfoTip: FunctionComponent = ({ level }) => { + return ; +}; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/kibana_status.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/kibana_status.test.ts index 63532543a418b..296a313abef38 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/kibana_status.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/kibana_status.test.ts @@ -20,7 +20,7 @@ const mockKibanaDeprecations: DomainDeprecationDetails[] = [ 'Using Kibana role-mapping management, change all role-mappings which assing the kibana_user role to the kibana_admin role.', ], }, - deprecationType: 'config', + deprecationType: 'feature', documentationUrl: 'testDocUrl', level: 'critical', message: 'testMessage', diff --git a/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts index 00b820a025c8b..2742fbff294c0 100644 --- a/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/recognize_module.ts @@ -44,7 +44,7 @@ export default ({ getService }: FtrProviderContext) => { user: USER.ML_POWERUSER, expected: { responseCode: 200, - moduleIds: ['apm_jsbase', 'apm_nodejs'], + moduleIds: ['apm_jsbase', 'apm_transaction', 'apm_nodejs'], }, }, { diff --git a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts index 6ff6b8113cb1a..c4dd529ac14f5 100644 --- a/x-pack/test/api_integration/apis/ml/modules/setup_module.ts +++ b/x-pack/test/api_integration/apis/ml/modules/setup_module.ts @@ -187,11 +187,9 @@ export default ({ getService }: FtrProviderContext) => { dashboards: [] as string[], }, }, - // Set startDatafeed and estimateModelMemory to false for the APM transaction test - // until there is a new data set available with metric data. { testTitleSuffix: - 'for apm_transaction with prefix, startDatafeed false and estimateModelMemory false', + 'for apm_transaction with prefix, startDatafeed true and estimateModelMemory true', sourceDataArchive: 'x-pack/test/functional/es_archives/ml/module_apm', indexPattern: { name: 'ft_module_apm', timeField: '@timestamp' }, module: 'apm_transaction', @@ -199,14 +197,14 @@ export default ({ getService }: FtrProviderContext) => { requestBody: { prefix: 'pf5_', indexPatternName: 'ft_module_apm', - startDatafeed: false, - estimateModelMemory: false, + startDatafeed: true, + end: Date.now(), }, expected: { responseCode: 200, jobs: [ { - jobId: 'pf5_apm_metrics', + jobId: 'pf5_high_mean_transaction_duration', jobState: JOB_STATE.CLOSED, datafeedState: DATAFEED_STATE.STOPPED, }, diff --git a/x-pack/test/fleet_api_integration/apis/epm/index.js b/x-pack/test/fleet_api_integration/apis/epm/index.js index b6a1fd5d7346d..3428b4c1ded08 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/index.js +++ b/x-pack/test/fleet_api_integration/apis/epm/index.js @@ -15,6 +15,7 @@ export default function loadTests({ loadTestFile }) { loadTestFile(require.resolve('./template')); loadTestFile(require.resolve('./ilm')); loadTestFile(require.resolve('./install_by_upload')); + loadTestFile(require.resolve('./install_endpoint')); loadTestFile(require.resolve('./install_overrides')); loadTestFile(require.resolve('./install_prerelease')); loadTestFile(require.resolve('./install_remove_assets')); diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_endpoint.ts b/x-pack/test/fleet_api_integration/apis/epm/install_endpoint.ts new file mode 100644 index 0000000000000..ba9264e1d1999 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/epm/install_endpoint.ts @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../../helpers'; +import { setupFleetAndAgents } from '../agents/services'; + +export default function (providerContext: FtrProviderContext) { + /** + * There are a few features that are only currently supported for the Endpoint + * package due to security concerns. + */ + describe('Install endpoint package', () => { + const { getService } = providerContext; + skipIfNoDockerRegistry(providerContext); + setupFleetAndAgents(providerContext); + + const supertest = getService('supertest'); + const dockerServers = getService('dockerServers'); + const server = dockerServers.get('registry'); + const es = getService('es'); + const pkgName = 'endpoint'; + let pkgVersion: string; + + const transforms = [ + { + id: 'endpoint.metadata_current-default', + dest: 'metrics-endpoint.metadata_current_default', + }, + { + id: 'endpoint.metadata_united-default', + dest: '.metrics-endpoint.metadata_united_default', + }, + ]; + + before(async () => { + if (!server.enabled) return; + // The latest endpoint package is already installed by default in our FTR config, + // just get the most recent version number. + const getResp = await supertest.get(`/api/fleet/epm/packages/${pkgName}`).expect(200); + pkgVersion = getResp.body.response.version; + }); + + describe('install', () => { + transforms.forEach((transform) => { + it(`should have installed the [${transform.id}] transform`, async function () { + const res = await es.transport.request({ + method: 'GET', + path: `/_transform/${transform.id}-${pkgVersion}`, + }); + expect(res.statusCode).equal(200); + }); + it(`should have created the destination index for the [${transform.id}] transform`, async function () { + // the index is defined in the transform file + const res = await es.transport.request({ + method: 'GET', + path: `/${transform.dest}`, + }); + expect(res.statusCode).equal(200); + }); + }); + }); + + const uninstallPackage = async (pkg: string) => + supertest.delete(`/api/fleet/epm/packages/${pkg}`).set('kbn-xsrf', 'xxxx'); + + // Endpoint doesn't currently support uninstalls + describe.skip('uninstall', () => { + before(async () => { + await uninstallPackage(`${pkgName}-${pkgVersion}`); + }); + + transforms.forEach((transform) => { + it(`should have uninstalled the [${transform.id}] transforms`, async function () { + const res = await es.transport.request( + { + method: 'GET', + path: `/_transform/${transform.id}`, + }, + { + ignore: [404], + } + ); + expect(res.statusCode).equal(404); + }); + + it(`should have deleted the index for the [${transform.id}] transform`, async function () { + // the index is defined in the transform file + const res = await es.transport.request( + { + method: 'GET', + path: `/${transform.dest}`, + }, + { + ignore: [404], + } + ); + expect(res.statusCode).equal(404); + }); + }); + }); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts index e57899531e939..7e48ed9d297c7 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_remove_assets.ts @@ -155,31 +155,6 @@ export default function (providerContext: FtrProviderContext) { ); expect(resPipeline2.statusCode).equal(404); }); - it('should have uninstalled the transforms', async function () { - const res = await es.transport.request( - { - method: 'GET', - path: `/_transform/${pkgName}-test-default-${pkgVersion}`, - }, - { - ignore: [404], - } - ); - expect(res.statusCode).equal(404); - }); - it('should have deleted the index for the transform', async function () { - // the index is defined in the transform file - const res = await es.transport.request( - { - method: 'GET', - path: `/logs-all_assets.test_log_current_default`, - }, - { - ignore: [404], - } - ); - expect(res.statusCode).equal(404); - }); it('should have uninstalled the kibana assets', async function () { let resDashboard; try { @@ -380,21 +355,6 @@ const expectAssetsInstalled = ({ }); expect(resUserSettings.statusCode).equal(200); }); - it('should have installed the transform components', async function () { - const res = await es.transport.request({ - method: 'GET', - path: `/_transform/${pkgName}.test-default-${pkgVersion}`, - }); - expect(res.statusCode).equal(200); - }); - it('should have created the index for the transform', async function () { - // the index is defined in the transform file - const res = await es.transport.request({ - method: 'GET', - path: `/logs-all_assets.test_log_current_default`, - }); - expect(res.statusCode).equal(200); - }); it('should have installed the kibana assets', async function () { // These are installed from Fleet along with every package const resIndexPatternLogs = await kibanaServer.savedObjects.get({ @@ -575,10 +535,6 @@ const expectAssetsInstalled = ({ id: 'logs-all_assets.test_logs-0.1.0-pipeline2', type: 'ingest_pipeline', }, - { - id: 'all_assets.test-default-0.1.0', - type: 'transform', - }, ], es_index_patterns: { test_logs: 'logs-all_assets.test_logs-*', @@ -597,7 +553,6 @@ const expectAssetsInstalled = ({ { id: 'f839c76e-d194-555a-90a1-3265a45789e4', type: 'epm-packages-assets' }, { id: '9af7bbb3-7d8a-50fa-acc9-9dde6f5efca2', type: 'epm-packages-assets' }, { id: '1e97a20f-9d1c-529b-8ff2-da4e8ba8bb71', type: 'epm-packages-assets' }, - { id: '8cfe0a2b-7016-5522-87e4-6d352360d1fc', type: 'epm-packages-assets' }, { id: 'bd5ff3c5-655e-5385-9918-b60ff3040aad', type: 'epm-packages-assets' }, { id: '0954ce3b-3165-5c1f-a4c0-56eb5f2fa487', type: 'epm-packages-assets' }, { id: '60d6d054-57e4-590f-a580-52bf3f5e7cca', type: 'epm-packages-assets' }, diff --git a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/elasticsearch/transform/test/default.json b/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/elasticsearch/transform/test/default.json deleted file mode 100644 index eddc6bc0c304a..0000000000000 --- a/x-pack/test/fleet_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/elasticsearch/transform/test/default.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "source": { - "index": "logs-all_assets.test_log-default*" - }, - "dest": { - "index": "logs-all_assets.test_log_current_default" - }, - "pivot": { - "group_by": { - "agent.id": { - "terms": { - "field": "agent.id" - } - } - }, - "aggregations": { - "HostDetails": { - "scripted_metric": { - "init_script": "state.timestamp_latest = 0L; state.last_doc=''", - "map_script": "def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli(); if (current_date \u003e state.timestamp_latest) {state.timestamp_latest = current_date;state.last_doc = new HashMap(params['_source']);}", - "combine_script": "return state", - "reduce_script": "def last_doc = '';def timestamp_latest = 0L; for (s in states) {if (s.timestamp_latest \u003e (timestamp_latest)) {timestamp_latest = s.timestamp_latest; last_doc = s.last_doc;}} return last_doc" - } - } - } - }, - "description": "collapse and update the latest document for each host", - "frequency": "1m", - "sync": { - "time": { - "field": "event.ingested", - "delay": "60s" - } - } -} diff --git a/x-pack/test/functional/apps/monitoring/enable_monitoring/index.js b/x-pack/test/functional/apps/monitoring/enable_monitoring/index.js index 79bd479c45a17..cce6401453d21 100644 --- a/x-pack/test/functional/apps/monitoring/enable_monitoring/index.js +++ b/x-pack/test/functional/apps/monitoring/enable_monitoring/index.js @@ -11,7 +11,6 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['monitoring', 'common', 'header']); const esSupertest = getService('esSupertest'); const noData = getService('monitoringNoData'); - const testSubjects = getService('testSubjects'); const clusterOverview = getService('monitoringClusterOverview'); const retry = getService('retry'); const esDeleteAllIndices = getService('esDeleteAllIndices'); @@ -53,8 +52,6 @@ export default function ({ getService, getPageObjects }) { // Here we are checking that once Monitoring is enabled, // it moves on to the cluster overview page. await retry.tryForTime(20000, async () => { - // Click the refresh button - await testSubjects.click('querySubmitButton'); await clusterOverview.closeAlertsModal(); expect(await clusterOverview.isOnClusterOverview()).to.be(true); }); diff --git a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts index 988bbdc621f5f..bf83892ce1934 100644 --- a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts +++ b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_security.ts @@ -13,6 +13,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const security = getService('security'); const appsMenu = getService('appsMenu'); const PageObjects = getPageObjects(['common', 'security']); + const noData = getService('monitoringNoData'); describe('security', () => { before(async () => { @@ -103,5 +104,32 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(navLinks).to.contain('Stack Monitoring'); }); }); + + describe('monitoring_user and kibana_admin roles', function () { + this.tags(['skipCloud']); + before(async () => { + await security.user.create('monitoring_kibana_admin_user', { + password: 'monitoring_user-password', + roles: ['monitoring_user', 'kibana_admin'], + full_name: 'monitoring user', + }); + + await PageObjects.security.login( + 'monitoring_kibana_admin_user', + 'monitoring_user-password' + ); + }); + + after(async () => { + await security.user.delete('monitoring_kibana_admin_user'); + }); + + it('denies enabling monitoring without enough permissions', async () => { + await PageObjects.common.navigateToApp('monitoring'); + await noData.isOnNoDataPage(); + await noData.clickSetupWithSelfMonitoring(); + expect(await noData.isOnNoDataPageMonitoringEnablementDenied()).to.be(true); + }); + }); }); } diff --git a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts index f2b872bccbaa7..71f100b49068f 100644 --- a/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts +++ b/x-pack/test/functional/apps/monitoring/feature_controls/monitoring_spaces.ts @@ -52,7 +52,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { basePath: '/s/custom_space', }); - const exists = await find.existsByCssSelector('monitoring-main'); + const exists = await find.existsByCssSelector('[data-test-subj="monitoringAppContainer"]'); expect(exists).to.be(true); }); }); diff --git a/x-pack/test/functional/services/monitoring/no_data.js b/x-pack/test/functional/services/monitoring/no_data.js index 7b4410425dcfe..bd34c45c2d293 100644 --- a/x-pack/test/functional/services/monitoring/no_data.js +++ b/x-pack/test/functional/services/monitoring/no_data.js @@ -30,5 +30,13 @@ export function MonitoringNoDataProvider({ getService }) { const pageId = await retry.try(() => testSubjects.find('noDataContainer')); return pageId !== null; } + + async isOnNoDataPageMonitoringEnablementDenied() { + return testSubjects.exists('weTriedContainer'); + } + + async clickSetupWithSelfMonitoring() { + await testSubjects.click('useInternalCollection'); + } })(); } diff --git a/x-pack/test/functional/services/observability/alerts/add_to_case.ts b/x-pack/test/functional/services/observability/alerts/add_to_case.ts new file mode 100644 index 0000000000000..1669fc69a683c --- /dev/null +++ b/x-pack/test/functional/services/observability/alerts/add_to_case.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const ADD_TO_EXISTING_CASE_SELECTOR = 'add-existing-case-menu-item'; +const ADD_TO_NEW_CASE_SELECTOR = 'add-new-case-item'; +const CREATE_CASE_FLYOUT = 'create-case-flyout'; +const SELECT_CASE_MODAL = 'all-cases-modal'; + +export function ObservabilityAlertsAddToCaseProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + const getAddToExistingCaseSelector = async () => { + return await testSubjects.find(ADD_TO_EXISTING_CASE_SELECTOR); + }; + + const getAddToExistingCaseSelectorOrFail = async () => { + return await testSubjects.existOrFail(ADD_TO_EXISTING_CASE_SELECTOR); + }; + + const missingAddToExistingCaseSelectorOrFail = async () => { + return await testSubjects.missingOrFail(ADD_TO_EXISTING_CASE_SELECTOR); + }; + + const getAddToNewCaseSelector = async () => { + return await testSubjects.find(ADD_TO_NEW_CASE_SELECTOR); + }; + + const getAddToNewCaseSelectorOrFail = async () => { + return await testSubjects.existOrFail(ADD_TO_NEW_CASE_SELECTOR); + }; + + const missingAddToNewCaseSelectorOrFail = async () => { + return await testSubjects.missingOrFail(ADD_TO_NEW_CASE_SELECTOR); + }; + + const addToNewCaseButtonClick = async () => { + return await (await getAddToNewCaseSelector()).click(); + }; + + const addToExistingCaseButtonClick = async () => { + return await (await getAddToExistingCaseSelector()).click(); + }; + + const getCreateCaseFlyoutOrFail = async () => { + return await testSubjects.existOrFail(CREATE_CASE_FLYOUT); + }; + + const closeFlyout = async () => { + return await (await testSubjects.find('euiFlyoutCloseButton')).click(); + }; + + const getAddtoExistingCaseModalOrFail = async () => { + return await testSubjects.existOrFail(SELECT_CASE_MODAL); + }; + + return { + getAddToExistingCaseSelector, + getAddToExistingCaseSelectorOrFail, + missingAddToExistingCaseSelectorOrFail, + getAddToNewCaseSelector, + getAddToNewCaseSelectorOrFail, + missingAddToNewCaseSelectorOrFail, + getCreateCaseFlyoutOrFail, + closeFlyout, + addToNewCaseButtonClick, + addToExistingCaseButtonClick, + getAddtoExistingCaseModalOrFail, + }; +} diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index 7098fdec2a9d4..d5a2ce2a18c41 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -204,5 +204,6 @@ export function ObservabilityAlertsCommonProvider({ setWorkflowStatusFilter, submitQuery, typeInQueryBar, + openActionsMenuForRow, }; } diff --git a/x-pack/test/functional/services/observability/alerts/index.ts b/x-pack/test/functional/services/observability/alerts/index.ts index f373b0d75c543..f2b5173dfe5b0 100644 --- a/x-pack/test/functional/services/observability/alerts/index.ts +++ b/x-pack/test/functional/services/observability/alerts/index.ts @@ -7,15 +7,17 @@ import { ObservabilityAlertsPaginationProvider } from './pagination'; import { ObservabilityAlertsCommonProvider } from './common'; +import { ObservabilityAlertsAddToCaseProvider } from './add_to_case'; import { FtrProviderContext } from '../../../ftr_provider_context'; export function ObservabilityAlertsProvider(context: FtrProviderContext) { const common = ObservabilityAlertsCommonProvider(context); const pagination = ObservabilityAlertsPaginationProvider(context); - + const addToCase = ObservabilityAlertsAddToCaseProvider(context); return { common, pagination, + addToCase, }; } diff --git a/x-pack/test/observability_functional/apps/observability/alerts/add_to_case.ts b/x-pack/test/observability_functional/apps/observability/alerts/add_to_case.ts new file mode 100644 index 0000000000000..f29111f2cb66b --- /dev/null +++ b/x-pack/test/observability_functional/apps/observability/alerts/add_to_case.ts @@ -0,0 +1,92 @@ +/* + * 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'; + +export default ({ getService, getPageObjects }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const observability = getService('observability'); + const retry = getService('retry'); + + describe('Observability alerts / Add to case', function () { + this.tags('includeFirefox'); + + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + + describe('When user has all priviledges for cases', () => { + before(async () => { + await observability.users.setTestUserRole( + observability.users.defineBasicObservabilityRole({ + observabilityCases: ['all'], + logs: ['all'], + }) + ); + await observability.alerts.common.navigateToTimeWithData(); + }); + + after(async () => { + await observability.users.restoreDefaultTestUserRole(); + }); + + it('renders case options in the overflow menu', async () => { + await observability.alerts.common.openActionsMenuForRow(0); + await retry.try(async () => { + await observability.alerts.addToCase.getAddToExistingCaseSelectorOrFail(); + await observability.alerts.addToCase.getAddToNewCaseSelectorOrFail(); + }); + }); + + it('opens a flyout when Add to new case is clicked', async () => { + await observability.alerts.addToCase.addToNewCaseButtonClick(); + + await retry.try(async () => { + await observability.alerts.addToCase.getCreateCaseFlyoutOrFail(); + await observability.alerts.addToCase.closeFlyout(); + }); + }); + + it('opens a modal when Add to existing case is clicked', async () => { + await observability.alerts.common.openActionsMenuForRow(0); + + await retry.try(async () => { + await observability.alerts.addToCase.addToExistingCaseButtonClick(); + await observability.alerts.addToCase.getAddtoExistingCaseModalOrFail(); + }); + }); + }); + + describe('When user has read permissions for cases', () => { + before(async () => { + await observability.users.setTestUserRole( + observability.users.defineBasicObservabilityRole({ + observabilityCases: ['read'], + logs: ['all'], + }) + ); + await observability.alerts.common.navigateToTimeWithData(); + }); + + after(async () => { + await observability.users.restoreDefaultTestUserRole(); + }); + + it('does not render case options in the overflow menu', async () => { + await observability.alerts.common.openActionsMenuForRow(0); + await retry.try(async () => { + await observability.alerts.addToCase.missingAddToExistingCaseSelectorOrFail(); + await observability.alerts.addToCase.missingAddToNewCaseSelectorOrFail(); + }); + }); + }); + }); +}; diff --git a/x-pack/test/observability_functional/apps/observability/index.ts b/x-pack/test/observability_functional/apps/observability/index.ts index b163d4d6bb8d5..43e056bae65c0 100644 --- a/x-pack/test/observability_functional/apps/observability/index.ts +++ b/x-pack/test/observability_functional/apps/observability/index.ts @@ -15,5 +15,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./alerts')); loadTestFile(require.resolve('./alerts/workflow_status')); loadTestFile(require.resolve('./alerts/pagination')); + loadTestFile(require.resolve('./alerts/add_to_case')); }); }