diff --git a/.eslintrc.js b/.eslintrc.js index ab868c29b7bed..a7b45534391c0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -93,6 +93,7 @@ const SAFER_LODASH_SET_DEFINITELYTYPED_HEADER = ` const DEV_PACKAGES = [ 'kbn-babel-code-parser', 'kbn-dev-utils', + 'kbn-cli-dev-mode', 'kbn-docs-utils', 'kbn-es*', 'kbn-eslint*', diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33b3e4a7dede6..f27885c1e32c3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -119,18 +119,23 @@ # Machine Learning /x-pack/plugins/ml/ @elastic/ml-ui +/x-pack/test/accessibility/apps/ml.ts @elastic/ml-ui +/x-pack/test/accessibility/apps/ml_embeddables_in_dashboard.ts @elastic/ml-ui +/x-pack/test/api_integration/apis/ml/ @elastic/ml-ui +/x-pack/test/api_integration_basic/apis/ml/ @elastic/ml-ui /x-pack/test/functional/apps/ml/ @elastic/ml-ui +/x-pack/test/functional/es_archives/ml/ @elastic/ml-ui /x-pack/test/functional/services/ml/ @elastic/ml-ui -/x-pack/test/accessibility/apps/ml.ts @elastic/ml-ui +/x-pack/test/functional_basic/apps/ml/ @elastic/ml-ui +/x-pack/test/functional_with_es_ssl/apps/ml/ @elastic/ml-ui + # ML team owns and maintains the transform plugin despite it living in the Elasticsearch management section. /x-pack/plugins/transform/ @elastic/ml-ui -/x-pack/test/functional/apps/transform/ @elastic/ml-ui -/x-pack/test/functional/services/transform/ @elastic/ml-ui /x-pack/test/accessibility/apps/transform.ts @elastic/ml-ui -/x-pack/test/api_integration_basic/apis/ml/ @elastic/ml-ui -/x-pack/test/functional_basic/apps/ml/ @elastic/ml-ui - +/x-pack/test/api_integration/apis/transform/ @elastic/ml-ui /x-pack/test/api_integration_basic/apis/transform/ @elastic/ml-ui +/x-pack/test/functional/apps/transform/ @elastic/ml-ui +/x-pack/test/functional/services/transform/ @elastic/ml-ui /x-pack/test/functional_basic/apps/transform/ @elastic/ml-ui # Maps @@ -158,6 +163,7 @@ /packages/kbn-ui-shared-deps/ @elastic/kibana-operations /packages/kbn-es-archiver/ @elastic/kibana-operations /packages/kbn-utils/ @elastic/kibana-operations +/packages/kbn-cli-dev-mode/ @elastic/kibana-operations /src/cli/keystore/ @elastic/kibana-operations /src/legacy/server/warnings/ @elastic/kibana-operations /.ci/es-snapshots/ @elastic/kibana-operations @@ -194,6 +200,8 @@ /packages/kbn-config/ @elastic/kibana-core /packages/kbn-logging/ @elastic/kibana-core /packages/kbn-legacy-logging/ @elastic/kibana-core +/packages/kbn-crypto/ @elastic/kibana-core +/packages/kbn-http-tools/ @elastic/kibana-core /src/legacy/server/config/ @elastic/kibana-core /src/legacy/server/http/ @elastic/kibana-core /src/legacy/server/logging/ @elastic/kibana-core diff --git a/api_docs/alerting.json b/api_docs/alerting.json index 5550798c4316f..f6e37bafdedb0 100644 --- a/api_docs/alerting.json +++ b/api_docs/alerting.json @@ -810,7 +810,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", - "lineNumber": 132 + "lineNumber": 133 } }, { @@ -821,7 +821,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", - "lineNumber": 133 + "lineNumber": 134 } }, { @@ -832,7 +832,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", - "lineNumber": 134 + "lineNumber": 135 } }, { @@ -843,7 +843,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", - "lineNumber": 135 + "lineNumber": 136 }, "signature": [ "Pick<", @@ -860,7 +860,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/alerts_client/alerts_client.ts", - "lineNumber": 131 + "lineNumber": 132 }, "initialIsOpen": false }, @@ -905,7 +905,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 111 + "lineNumber": 93 } } ], @@ -913,13 +913,13 @@ "returnComment": [], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 103 + "lineNumber": 85 } } ], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 102 + "lineNumber": 84 }, "initialIsOpen": false }, @@ -938,7 +938,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 123 + "lineNumber": 105 }, "signature": [ "() => Set<", @@ -994,7 +994,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 124 + "lineNumber": 106 } } ], @@ -1002,7 +1002,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 124 + "lineNumber": 106 } }, { @@ -1013,7 +1013,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 125 + "lineNumber": 107 }, "signature": [ "() => Promise<", @@ -1030,7 +1030,7 @@ ], "source": { "path": "x-pack/plugins/alerting/server/plugin.ts", - "lineNumber": 122 + "lineNumber": 104 }, "initialIsOpen": false } @@ -1118,7 +1118,7 @@ }, ", \"enabled\" | \"id\" | \"name\" | \"params\" | \"actions\" | \"muteAll\" | \"tags\" | \"alertTypeId\" | \"consumer\" | \"schedule\" | \"scheduledTaskId\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"apiKeyOwner\" | \"throttle\" | \"notifyWhen\" | \"mutedInstanceIds\" | \"executionStatus\">>; delete: ({ id }: { id: string; }) => Promise<{}>; create: = never>({ data, options, }: ", "CreateOptions", - ") => Promise<", + ") => Promise>; find: ({ options: { fields, ...options }, }?: { options?: ", + ", \"enabled\" | \"id\" | \"name\" | \"params\" | \"actions\" | \"muteAll\" | \"tags\" | \"alertTypeId\" | \"consumer\" | \"schedule\" | \"scheduledTaskId\" | \"createdBy\" | \"updatedBy\" | \"createdAt\" | \"updatedAt\" | \"apiKeyOwner\" | \"throttle\" | \"notifyWhen\" | \"mutedInstanceIds\" | \"executionStatus\">>; find: ({ options: { fields, ...options }, }?: { options?: ", "FindOptions", " | undefined; }) => Promise<", { @@ -2922,7 +2922,7 @@ "description": [], "source": { "path": "x-pack/plugins/alerting/common/index.ts", - "lineNumber": 28 + "lineNumber": 30 }, "signature": [ "\"alerts\"" @@ -3006,16 +3006,16 @@ }, { "tags": [], - "id": "def-common.BASE_ALERT_API_PATH", + "id": "def-common.BASE_ALERTING_API_PATH", "type": "string", - "label": "BASE_ALERT_API_PATH", + "label": "BASE_ALERTING_API_PATH", "description": [], "source": { "path": "x-pack/plugins/alerting/common/index.ts", - "lineNumber": 27 + "lineNumber": 28 }, "signature": [ - "\"/api/alerts\"" + "\"/api/alerting\"" ], "initialIsOpen": false }, @@ -3034,6 +3034,36 @@ ], "initialIsOpen": false }, + { + "tags": [], + "id": "def-common.INTERNAL_BASE_ALERTING_API_PATH", + "type": "string", + "label": "INTERNAL_BASE_ALERTING_API_PATH", + "description": [], + "source": { + "path": "x-pack/plugins/alerting/common/index.ts", + "lineNumber": 29 + }, + "signature": [ + "\"/internal/alerting\"" + ], + "initialIsOpen": false + }, + { + "tags": [], + "id": "def-common.LEGACY_BASE_ALERT_API_PATH", + "type": "string", + "label": "LEGACY_BASE_ALERT_API_PATH", + "description": [], + "source": { + "path": "x-pack/plugins/alerting/common/index.ts", + "lineNumber": 27 + }, + "signature": [ + "\"/api/alerts\"" + ], + "initialIsOpen": false + }, { "id": "def-common.RawAlertInstance", "type": "Type", diff --git a/api_docs/apm.json b/api_docs/apm.json index ac26de577cf0f..7ddf4f2548d84 100644 --- a/api_docs/apm.json +++ b/api_docs/apm.json @@ -747,7 +747,7 @@ "text": "APMEventESSearchRequest" }, ">(params: TParams, { includeLegacyData }?: { includeLegacyData?: boolean | undefined; }): Promise<", - "ESSearchResponse", + "InferSearchResponseOf", " void; }" + "{ addProcessorDefinition: (processor: unknown) => void; }" ], "lifecycle": "start", "initialIsOpen": true diff --git a/api_docs/core.json b/api_docs/core.json index e02bd33d6671b..cb416cba80078 100644 --- a/api_docs/core.json +++ b/api_docs/core.json @@ -1431,7 +1431,7 @@ "description": [], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 300 + "lineNumber": 302 } }, { @@ -1442,7 +1442,7 @@ "description": [], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 301 + "lineNumber": 303 } }, { @@ -1453,7 +1453,7 @@ "description": [], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 302 + "lineNumber": 304 }, "signature": [ "{ readonly dashboard: { readonly guide: string; readonly drilldowns: string; readonly drilldownsTriggerPicker: string; readonly urlDrilldownTemplateSyntax: string; readonly urlDrilldownVariables: string; }; readonly discover: Record; readonly filebeat: { readonly base: string; readonly installation: string; readonly configuration: string; readonly elasticsearchOutput: string; readonly elasticsearchModule: string; readonly startup: string; readonly exportedFields: string; }; readonly auditbeat: { readonly base: 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 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: 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 indexPatterns: { readonly introduction: string; readonly fieldFormattersNumber: string; readonly fieldFormattersString: string; }; readonly addData: string; readonly kibana: string; readonly elasticsearch: Record; readonly siem: { readonly guide: string; readonly gettingStarted: string; }; readonly query: { readonly eql: string; readonly luceneQuerySyntax: string; readonly queryDsl: string; readonly kueryQuerySyntax: string; }; readonly date: { readonly dateMath: string; readonly dateMathIndexNames: string; }; readonly management: Record; readonly ml: Record; readonly transforms: Record; readonly visualize: Record; readonly apis: Readonly<{ createIndex: string; createSnapshotLifecyclePolicy: string; createRoleMapping: string; createRoleMappingTemplates: 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; putWatch: string; updateTransform: string; }>; readonly observability: Record; readonly alerting: Record; readonly maps: Record; readonly monitoring: Record; 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; readonly ccs: Record; readonly plugins: Record; readonly snapshotRestore: Record; readonly ingest: Record; }" @@ -1462,7 +1462,7 @@ ], "source": { "path": "src/core/public/doc_links/doc_links_service.ts", - "lineNumber": 299 + "lineNumber": 301 }, "initialIsOpen": false }, @@ -3833,7 +3833,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 141 + "lineNumber": 142 }, "signature": [ "string | undefined" @@ -3842,7 +3842,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 139 + "lineNumber": 140 }, "initialIsOpen": false }, @@ -3865,7 +3865,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 78 + "lineNumber": 79 }, "signature": [ "string | string[]" @@ -3879,7 +3879,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 79 + "lineNumber": 80 }, "signature": [ "number | undefined" @@ -3893,7 +3893,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 80 + "lineNumber": 81 }, "signature": [ "number | undefined" @@ -3907,7 +3907,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 81 + "lineNumber": 82 }, "signature": [ "string | undefined" @@ -3916,15 +3916,15 @@ { "tags": [], "id": "def-public.SavedObjectsFindOptions.sortOrder", - "type": "string", + "type": "CompoundType", "label": "sortOrder", "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 82 + "lineNumber": 83 }, "signature": [ - "string | undefined" + "\"asc\" | \"desc\" | \"_doc\" | undefined" ] }, { @@ -3937,7 +3937,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 88 + "lineNumber": 89 }, "signature": [ "string[] | undefined" @@ -3953,7 +3953,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 90 + "lineNumber": 91 }, "signature": [ "string | undefined" @@ -3969,7 +3969,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 92 + "lineNumber": 93 }, "signature": [ "string[] | undefined" @@ -3985,10 +3985,10 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 96 + "lineNumber": 97 }, "signature": [ - "unknown[] | undefined" + "string[] | undefined" ] }, { @@ -4001,7 +4001,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 101 + "lineNumber": 102 }, "signature": [ "string[] | undefined" @@ -4017,7 +4017,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 107 + "lineNumber": 108 }, "signature": [ { @@ -4048,7 +4048,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 111 + "lineNumber": 112 }, "signature": [ "\"AND\" | \"OR\" | undefined" @@ -4064,7 +4064,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 116 + "lineNumber": 117 }, "signature": [ "\"AND\" | \"OR\" | undefined" @@ -4078,7 +4078,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 117 + "lineNumber": 118 }, "signature": [ "any" @@ -4092,7 +4092,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 118 + "lineNumber": 119 }, "signature": [ "string[] | undefined" @@ -4108,7 +4108,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 126 + "lineNumber": 127 }, "signature": [ "Map | undefined" @@ -4124,7 +4124,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 128 + "lineNumber": 129 }, "signature": [ "string | undefined" @@ -4140,7 +4140,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 132 + "lineNumber": 133 }, "signature": [ { @@ -4156,7 +4156,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 77 + "lineNumber": 78 }, "initialIsOpen": false }, @@ -4177,7 +4177,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 61 + "lineNumber": 62 } }, { @@ -4188,13 +4188,13 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 62 + "lineNumber": 63 } } ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 60 + "lineNumber": 61 }, "initialIsOpen": false }, @@ -5753,7 +5753,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 226 + "lineNumber": 227 }, "signature": [ "\"multiple\" | \"single\" | \"multiple-isolated\" | \"agnostic\"" @@ -8002,7 +8002,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 459 + "lineNumber": 462 }, "signature": [ { @@ -8024,7 +8024,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 461 + "lineNumber": 464 }, "signature": [ { @@ -8046,7 +8046,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 463 + "lineNumber": 466 }, "signature": [ { @@ -8068,7 +8068,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 465 + "lineNumber": 468 }, "signature": [ { @@ -8099,7 +8099,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 470 + "lineNumber": 473 }, "signature": [ { @@ -8121,7 +8121,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 472 + "lineNumber": 475 }, "signature": [ { @@ -8143,7 +8143,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 474 + "lineNumber": 477 }, "signature": [ { @@ -8165,7 +8165,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 476 + "lineNumber": 479 }, "signature": [ { @@ -8187,7 +8187,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 478 + "lineNumber": 481 }, "signature": [ { @@ -8209,7 +8209,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 480 + "lineNumber": 483 }, "signature": [ { @@ -8231,7 +8231,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 482 + "lineNumber": 485 }, "signature": [ { @@ -8247,7 +8247,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 457 + "lineNumber": 460 }, "initialIsOpen": false }, @@ -8272,7 +8272,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 505 + "lineNumber": 508 }, "signature": [ { @@ -8294,7 +8294,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 507 + "lineNumber": 510 }, "signature": [ { @@ -8316,7 +8316,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 509 + "lineNumber": 512 }, "signature": [ { @@ -8338,7 +8338,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 511 + "lineNumber": 514 }, "signature": [ { @@ -8360,7 +8360,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 513 + "lineNumber": 516 }, "signature": [ { @@ -8382,7 +8382,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 515 + "lineNumber": 518 }, "signature": [ { @@ -8397,7 +8397,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 503 + "lineNumber": 506 }, "initialIsOpen": false }, @@ -14272,7 +14272,7 @@ "description": [], "source": { "path": "src/core/server/index.ts", - "lineNumber": 425 + "lineNumber": 428 }, "signature": [ "{ savedObjects: { client: Pick<", @@ -14283,7 +14283,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">; typeRegistry: Pick<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">; typeRegistry: Pick<", { "pluginId": "core", "scope": "server", @@ -14307,7 +14307,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">; getExporter: (client: Pick<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">; getExporter: (client: Pick<", { "pluginId": "core", "scope": "server", @@ -14320,7 +14320,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 424 + "lineNumber": 427 }, "initialIsOpen": false }, @@ -15689,7 +15689,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">) => ", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">) => ", { "pluginId": "core", "scope": "server", @@ -15715,7 +15715,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ], "description": [], "source": { @@ -15965,7 +15965,7 @@ "lineNumber": 22 }, "signature": [ - "Pick & { transport: { request(params: TransportRequestParams, options?: TransportRequestOptions | undefined): TransportRequestPromise; }; }" + "Pick & { transport: { request(params: TransportRequestParams, options?: TransportRequestOptions | undefined): TransportRequestPromise; }; }" ], "initialIsOpen": false }, @@ -16616,7 +16616,7 @@ ], "source": { "path": "src/core/server/index.ts", - "lineNumber": 493 + "lineNumber": 496 }, "signature": [ "() => Promise<[", diff --git a/api_docs/core_http.json b/api_docs/core_http.json index 8053550cc0e80..ce5ceb2840ec7 100644 --- a/api_docs/core_http.json +++ b/api_docs/core_http.json @@ -974,7 +974,7 @@ "lineNumber": 197 }, "signature": [ - "\"error\" | \"manual\" | \"follow\" | undefined" + "\"error\" | \"follow\" | \"manual\" | undefined" ] }, { diff --git a/api_docs/core_saved_objects.json b/api_docs/core_saved_objects.json index d862df7ef10bb..54f13f3911be6 100644 --- a/api_docs/core_saved_objects.json +++ b/api_docs/core_saved_objects.json @@ -1530,7 +1530,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 397 + "lineNumber": 402 }, "signature": [ "typeof ", @@ -1551,7 +1551,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 398 + "lineNumber": 403 }, "signature": [ "typeof ", @@ -1601,7 +1601,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 414 + "lineNumber": 419 } }, { @@ -1614,7 +1614,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 414 + "lineNumber": 419 } }, { @@ -1634,7 +1634,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 414 + "lineNumber": 419 } } ], @@ -1642,7 +1642,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 414 + "lineNumber": 419 } }, { @@ -1697,7 +1697,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 425 + "lineNumber": 430 } }, { @@ -1717,7 +1717,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 426 + "lineNumber": 431 } } ], @@ -1725,7 +1725,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 424 + "lineNumber": 429 } }, { @@ -1780,7 +1780,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 439 + "lineNumber": 444 } }, { @@ -1799,7 +1799,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 440 + "lineNumber": 445 } } ], @@ -1807,7 +1807,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 438 + "lineNumber": 443 } }, { @@ -1839,7 +1839,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 452 + "lineNumber": 457 } }, { @@ -1852,7 +1852,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 452 + "lineNumber": 457 } }, { @@ -1871,7 +1871,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 452 + "lineNumber": 457 } } ], @@ -1879,7 +1879,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 452 + "lineNumber": 457 } }, { @@ -1925,7 +1925,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 461 + "lineNumber": 466 } } ], @@ -1933,7 +1933,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 461 + "lineNumber": 466 } }, { @@ -1990,7 +1990,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 477 + "lineNumber": 482 } }, { @@ -2009,7 +2009,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 478 + "lineNumber": 483 } } ], @@ -2017,7 +2017,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 476 + "lineNumber": 481 } }, { @@ -2059,7 +2059,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 491 + "lineNumber": 496 } }, { @@ -2074,7 +2074,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 492 + "lineNumber": 497 } }, { @@ -2093,7 +2093,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 493 + "lineNumber": 498 } } ], @@ -2101,7 +2101,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 490 + "lineNumber": 495 } }, { @@ -2143,7 +2143,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 506 + "lineNumber": 511 } }, { @@ -2158,7 +2158,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 507 + "lineNumber": 512 } }, { @@ -2177,7 +2177,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 508 + "lineNumber": 513 } } ], @@ -2185,7 +2185,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 505 + "lineNumber": 510 } }, { @@ -2225,7 +2225,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 521 + "lineNumber": 526 } }, { @@ -2238,7 +2238,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 522 + "lineNumber": 527 } }, { @@ -2251,7 +2251,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 523 + "lineNumber": 528 } }, { @@ -2270,7 +2270,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 524 + "lineNumber": 529 } } ], @@ -2278,7 +2278,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 520 + "lineNumber": 525 } }, { @@ -2318,7 +2318,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 538 + "lineNumber": 543 } }, { @@ -2331,7 +2331,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 539 + "lineNumber": 544 } }, { @@ -2344,7 +2344,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 540 + "lineNumber": 545 } }, { @@ -2363,7 +2363,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 541 + "lineNumber": 546 } } ], @@ -2371,7 +2371,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 537 + "lineNumber": 542 } }, { @@ -2411,7 +2411,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 555 + "lineNumber": 560 } }, { @@ -2424,7 +2424,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 556 + "lineNumber": 561 } }, { @@ -2437,7 +2437,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 557 + "lineNumber": 562 } }, { @@ -2456,7 +2456,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 558 + "lineNumber": 563 } } ], @@ -2464,7 +2464,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 554 + "lineNumber": 559 } }, { @@ -2519,7 +2519,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 569 + "lineNumber": 574 } }, { @@ -2539,7 +2539,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 570 + "lineNumber": 575 } } ], @@ -2547,7 +2547,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 568 + "lineNumber": 573 } }, { @@ -2587,7 +2587,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 579 + "lineNumber": 584 } }, { @@ -2600,7 +2600,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 580 + "lineNumber": 585 } }, { @@ -2620,7 +2620,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 581 + "lineNumber": 586 } } ], @@ -2628,7 +2628,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 578 + "lineNumber": 583 } }, { @@ -2655,7 +2655,7 @@ ">" ], "description": [ - "\nOpens a Point In Time (PIT) against the indices for the specified Saved Object types.\nThe returned `id` can then be passed to {@link SavedObjectsClient.find} to search\nagainst that PIT." + "\nOpens a Point In Time (PIT) against the indices for the specified Saved Object types.\nThe returned `id` can then be passed to {@link SavedObjectsClient.find} to search\nagainst that PIT.\n\nOnly use this API if you have an advanced use case that's not solved by the\n{@link SavedObjectsClient.createPointInTimeFinder} method." ], "children": [ { @@ -2668,7 +2668,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 592 + "lineNumber": 600 } }, { @@ -2687,7 +2687,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 593 + "lineNumber": 601 } } ], @@ -2695,7 +2695,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 591 + "lineNumber": 599 } }, { @@ -2722,7 +2722,7 @@ ">" ], "description": [ - "\nCloses a Point In Time (PIT) by ID. This simply proxies the request to ES via the\nElasticsearch client, and is included in the Saved Objects Client as a convenience\nfor consumers who are using {@link SavedObjectsClient.openPointInTimeForType}." + "\nCloses a Point In Time (PIT) by ID. This simply proxies the request to ES via the\nElasticsearch client, and is included in the Saved Objects Client as a convenience\nfor consumers who are using {@link SavedObjectsClient.openPointInTimeForType}.\n\nOnly use this API if you have an advanced use case that's not solved by the\n{@link SavedObjectsClient.createPointInTimeFinder} method." ], "children": [ { @@ -2735,7 +2735,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 603 + "lineNumber": 614 } }, { @@ -2755,7 +2755,90 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 603 + "lineNumber": 614 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "src/core/server/saved_objects/service/saved_objects_client.ts", + "lineNumber": 614 + } + }, + { + "id": "def-server.SavedObjectsClient.createPointInTimeFinder", + "type": "Function", + "label": "createPointInTimeFinder", + "signature": [ + "(findOptions: Pick<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptions", + "text": "SavedObjectsFindOptions" + }, + ", \"type\" | \"filter\" | \"fields\" | \"search\" | \"perPage\" | \"sortField\" | \"sortOrder\" | \"searchFields\" | \"rootSearchFields\" | \"hasReference\" | \"hasReferenceOperator\" | \"defaultSearchOperator\" | \"namespaces\" | \"typeToNamespacesMap\" | \"preference\">, dependencies?: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsCreatePointInTimeFinderDependencies", + "text": "SavedObjectsCreatePointInTimeFinderDependencies" + }, + " | undefined) => ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.ISavedObjectsPointInTimeFinder", + "text": "ISavedObjectsPointInTimeFinder" + } + ], + "description": [ + "\nReturns a {@link ISavedObjectsPointInTimeFinder} to help page through\nlarge sets of saved objects. We strongly recommend using this API for\nany `find` queries that might return more than 1000 saved objects,\nhowever this API is only intended for use in server-side \"batch\"\nprocessing of objects where you are collecting all objects in memory\nor streaming them back to the client.\n\nDo NOT use this API in a route handler to facilitate paging through\nsaved objects on the client-side unless you are streaming all of the\nresults back to the client at once. Because the returned generator is\nstateful, you cannot rely on subsequent http requests retrieving new\npages from the same Kibana server in multi-instance deployments.\n\nThe generator wraps calls to {@link SavedObjectsClient.find} and iterates\nover multiple pages of results using `_pit` and `search_after`. This will\nopen a new Point-In-Time (PIT), and continue paging until a set of\nresults is received that's smaller than the designated `perPage`.\n\nOnce you have retrieved all of the results you need, it is recommended\nto call `close()` to clean up the PIT and prevent Elasticsearch from\nconsuming resources unnecessarily. This is only required if you are\ndone iterating and have not yet paged through all of the results: the\nPIT will automatically be closed for you once you reach the last page\nof results, or if the underlying call to `find` fails for any reason.\n" + ], + "children": [ + { + "type": "Object", + "label": "findOptions", + "isRequired": true, + "signature": [ + "Pick<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptions", + "text": "SavedObjectsFindOptions" + }, + ", \"type\" | \"filter\" | \"fields\" | \"search\" | \"perPage\" | \"sortField\" | \"sortOrder\" | \"searchFields\" | \"rootSearchFields\" | \"hasReference\" | \"hasReferenceOperator\" | \"defaultSearchOperator\" | \"namespaces\" | \"typeToNamespacesMap\" | \"preference\">" + ], + "description": [], + "source": { + "path": "src/core/server/saved_objects/service/saved_objects_client.ts", + "lineNumber": 664 + } + }, + { + "type": "Object", + "label": "dependencies", + "isRequired": false, + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsCreatePointInTimeFinderDependencies", + "text": "SavedObjectsCreatePointInTimeFinderDependencies" + }, + " | undefined" + ], + "description": [], + "source": { + "path": "src/core/server/saved_objects/service/saved_objects_client.ts", + "lineNumber": 665 } } ], @@ -2763,13 +2846,13 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 603 + "lineNumber": 663 } } ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 396 + "lineNumber": 401 }, "initialIsOpen": false }, @@ -4167,7 +4250,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 38 + "lineNumber": 37 }, "signature": [ "Pick<", @@ -4178,7 +4261,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { @@ -4189,7 +4272,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 39 + "lineNumber": 38 }, "signature": [ "Record" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { @@ -4274,7 +4357,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 50 + "lineNumber": 49 }, "signature": [ "Pick<", @@ -4296,7 +4379,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 51 + "lineNumber": 50 } }, { @@ -4307,7 +4390,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 52 + "lineNumber": 51 }, "signature": [ "Logger" @@ -4316,7 +4399,7 @@ ], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 48 + "lineNumber": 47 } } ], @@ -4324,7 +4407,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 43 + "lineNumber": 42 } }, { @@ -4364,7 +4447,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 75 + "lineNumber": 74 } } ], @@ -4374,7 +4457,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 75 + "lineNumber": 74 } }, { @@ -4414,7 +4497,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 93 + "lineNumber": 92 } } ], @@ -4424,13 +4507,13 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 93 + "lineNumber": 92 } } ], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 37 + "lineNumber": 36 }, "initialIsOpen": false }, @@ -4727,7 +4810,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { @@ -4820,7 +4903,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { @@ -5258,7 +5341,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 237 + "lineNumber": 257 } }, { @@ -5271,7 +5354,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 238 + "lineNumber": 258 } }, { @@ -5290,7 +5373,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 239 + "lineNumber": 259 } } ], @@ -5302,7 +5385,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 236 + "lineNumber": 256 } }, { @@ -5359,7 +5442,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 330 + "lineNumber": 350 } }, { @@ -5378,7 +5461,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 331 + "lineNumber": 351 } } ], @@ -5390,7 +5473,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 329 + "lineNumber": 349 } }, { @@ -5445,7 +5528,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 517 + "lineNumber": 541 } }, { @@ -5464,7 +5547,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 518 + "lineNumber": 542 } } ], @@ -5472,7 +5555,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 516 + "lineNumber": 540 } }, { @@ -5504,7 +5587,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 602 + "lineNumber": 627 } }, { @@ -5517,7 +5600,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 602 + "lineNumber": 627 } }, { @@ -5536,7 +5619,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 602 + "lineNumber": 627 } } ], @@ -5546,7 +5629,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 602 + "lineNumber": 627 } }, { @@ -5578,7 +5661,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 664 + "lineNumber": 690 } }, { @@ -5597,7 +5680,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 665 + "lineNumber": 691 } } ], @@ -5607,7 +5690,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 663 + "lineNumber": 689 } }, { @@ -5651,7 +5734,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 725 + "lineNumber": 751 } } ], @@ -5663,7 +5746,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 725 + "lineNumber": 751 } }, { @@ -5720,7 +5803,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 875 + "lineNumber": 906 } }, { @@ -5739,7 +5822,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 876 + "lineNumber": 907 } } ], @@ -5751,7 +5834,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 874 + "lineNumber": 905 } }, { @@ -5791,7 +5874,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 960 + "lineNumber": 993 } }, { @@ -5804,7 +5887,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 961 + "lineNumber": 994 } }, { @@ -5823,7 +5906,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 962 + "lineNumber": 995 } } ], @@ -5835,7 +5918,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 959 + "lineNumber": 992 } }, { @@ -5875,7 +5958,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 998 + "lineNumber": 1035 } }, { @@ -5888,7 +5971,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 999 + "lineNumber": 1036 } }, { @@ -5907,7 +5990,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1000 + "lineNumber": 1037 } } ], @@ -5919,7 +6002,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 997 + "lineNumber": 1034 } }, { @@ -5959,7 +6042,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1117 + "lineNumber": 1160 } }, { @@ -5972,7 +6055,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1118 + "lineNumber": 1161 } }, { @@ -5985,7 +6068,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1119 + "lineNumber": 1162 } }, { @@ -6004,7 +6087,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1120 + "lineNumber": 1163 } } ], @@ -6014,7 +6097,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1116 + "lineNumber": 1159 } }, { @@ -6054,7 +6137,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1190 + "lineNumber": 1232 } }, { @@ -6067,7 +6150,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1191 + "lineNumber": 1233 } }, { @@ -6080,7 +6163,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1192 + "lineNumber": 1234 } }, { @@ -6099,7 +6182,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1193 + "lineNumber": 1235 } } ], @@ -6107,7 +6190,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1189 + "lineNumber": 1231 } }, { @@ -6147,7 +6230,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1253 + "lineNumber": 1295 } }, { @@ -6160,7 +6243,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1254 + "lineNumber": 1296 } }, { @@ -6173,7 +6256,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1255 + "lineNumber": 1297 } }, { @@ -6192,7 +6275,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1256 + "lineNumber": 1298 } } ], @@ -6200,7 +6283,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1252 + "lineNumber": 1294 } }, { @@ -6257,7 +6340,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1358 + "lineNumber": 1401 } }, { @@ -6276,7 +6359,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1359 + "lineNumber": 1402 } } ], @@ -6288,7 +6371,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1357 + "lineNumber": 1400 } }, { @@ -6328,7 +6411,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1573 + "lineNumber": 1620 } }, { @@ -6341,7 +6424,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1574 + "lineNumber": 1621 } }, { @@ -6360,7 +6443,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1575 + "lineNumber": 1622 } } ], @@ -6368,7 +6451,7 @@ "returnComment": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1572 + "lineNumber": 1619 } }, { @@ -6392,7 +6475,7 @@ "section": "def-server.SavedObjectsIncrementCounterOptions", "text": "SavedObjectsIncrementCounterOptions" }, - ") => Promise<", + ") => Promise<", { "pluginId": "core", "scope": "common", @@ -6418,7 +6501,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1670 + "lineNumber": 1731 } }, { @@ -6433,7 +6516,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1671 + "lineNumber": 1732 } }, { @@ -6456,7 +6539,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1672 + "lineNumber": 1733 } }, { @@ -6470,14 +6553,15 @@ "docId": "kibCoreSavedObjectsPluginApi", "section": "def-server.SavedObjectsIncrementCounterOptions", "text": "SavedObjectsIncrementCounterOptions" - } + }, + "" ], "description": [ "- {@link SavedObjectsIncrementCounterOptions}" ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1673 + "lineNumber": 1734 } } ], @@ -6487,7 +6571,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1669 + "lineNumber": 1730 } }, { @@ -6514,7 +6598,7 @@ ">" ], "description": [ - "\nOpens a Point In Time (PIT) against the indices for the specified Saved Object types.\nThe returned `id` can then be passed to `SavedObjects.find` to search against that PIT.\n" + "\nOpens a Point In Time (PIT) against the indices for the specified Saved Object types.\nThe returned `id` can then be passed to `SavedObjects.find` to search against that PIT.\n\nOnly use this API if you have an advanced use case that's not solved by the\n{@link SavedObjectsRepository.createPointInTimeFinder} method.\n" ], "children": [ { @@ -6527,7 +6611,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1821 + "lineNumber": 1891 } }, { @@ -6546,7 +6630,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1822 + "lineNumber": 1892 } } ], @@ -6558,7 +6642,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1820 + "lineNumber": 1890 } }, { @@ -6585,7 +6669,7 @@ ">" ], "description": [ - "\nCloses a Point In Time (PIT) by ID. This simply proxies the request to ES\nvia the Elasticsearch client, and is included in the Saved Objects Client\nas a convenience for consumers who are using `openPointInTimeForType`.\n" + "\nCloses a Point In Time (PIT) by ID. This simply proxies the request to ES\nvia the Elasticsearch client, and is included in the Saved Objects Client\nas a convenience for consumers who are using `openPointInTimeForType`.\n\nOnly use this API if you have an advanced use case that's not solved by the\n{@link SavedObjectsRepository.createPointInTimeFinder} method.\n" ], "children": [ { @@ -6598,7 +6682,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1890 + "lineNumber": 1967 } }, { @@ -6620,7 +6704,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1891 + "lineNumber": 1968 } } ], @@ -6630,13 +6714,96 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 1889 + "lineNumber": 1966 + } + }, + { + "id": "def-server.SavedObjectsRepository.createPointInTimeFinder", + "type": "Function", + "label": "createPointInTimeFinder", + "signature": [ + "(findOptions: Pick<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptions", + "text": "SavedObjectsFindOptions" + }, + ", \"type\" | \"filter\" | \"fields\" | \"search\" | \"perPage\" | \"sortField\" | \"sortOrder\" | \"searchFields\" | \"rootSearchFields\" | \"hasReference\" | \"hasReferenceOperator\" | \"defaultSearchOperator\" | \"namespaces\" | \"typeToNamespacesMap\" | \"preference\">, dependencies?: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsCreatePointInTimeFinderDependencies", + "text": "SavedObjectsCreatePointInTimeFinderDependencies" + }, + " | undefined) => ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.ISavedObjectsPointInTimeFinder", + "text": "ISavedObjectsPointInTimeFinder" + } + ], + "description": [ + "\nReturns a {@link ISavedObjectsPointInTimeFinder} to help page through\nlarge sets of saved objects. We strongly recommend using this API for\nany `find` queries that might return more than 1000 saved objects,\nhowever this API is only intended for use in server-side \"batch\"\nprocessing of objects where you are collecting all objects in memory\nor streaming them back to the client.\n\nDo NOT use this API in a route handler to facilitate paging through\nsaved objects on the client-side unless you are streaming all of the\nresults back to the client at once. Because the returned generator is\nstateful, you cannot rely on subsequent http requests retrieving new\npages from the same Kibana server in multi-instance deployments.\n\nThis generator wraps calls to {@link SavedObjectsRepository.find} and\niterates over multiple pages of results using `_pit` and `search_after`.\nThis will open a new Point-In-Time (PIT), and continue paging until a\nset of results is received that's smaller than the designated `perPage`.\n\nOnce you have retrieved all of the results you need, it is recommended\nto call `close()` to clean up the PIT and prevent Elasticsearch from\nconsuming resources unnecessarily. This is only required if you are\ndone iterating and have not yet paged through all of the results: the\nPIT will automatically be closed for you once you reach the last page\nof results, or if the underlying call to `find` fails for any reason.\n" + ], + "children": [ + { + "type": "Object", + "label": "findOptions", + "isRequired": true, + "signature": [ + "Pick<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptions", + "text": "SavedObjectsFindOptions" + }, + ", \"type\" | \"filter\" | \"fields\" | \"search\" | \"perPage\" | \"sortField\" | \"sortOrder\" | \"searchFields\" | \"rootSearchFields\" | \"hasReference\" | \"hasReferenceOperator\" | \"defaultSearchOperator\" | \"namespaces\" | \"typeToNamespacesMap\" | \"preference\">" + ], + "description": [], + "source": { + "path": "src/core/server/saved_objects/service/lib/repository.ts", + "lineNumber": 2023 + } + }, + { + "type": "Object", + "label": "dependencies", + "isRequired": false, + "signature": [ + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsCreatePointInTimeFinderDependencies", + "text": "SavedObjectsCreatePointInTimeFinderDependencies" + }, + " | undefined" + ], + "description": [], + "source": { + "path": "src/core/server/saved_objects/service/lib/repository.ts", + "lineNumber": 2024 + } + } + ], + "tags": [], + "returnComment": [], + "source": { + "path": "src/core/server/saved_objects/service/lib/repository.ts", + "lineNumber": 2022 } } ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 143 + "lineNumber": 158 }, "initialIsOpen": false }, @@ -7617,6 +7784,62 @@ ], "functions": [], "interfaces": [ + { + "id": "def-server.ISavedObjectsPointInTimeFinder", + "type": "Interface", + "label": "ISavedObjectsPointInTimeFinder", + "description": [], + "tags": [ + "public" + ], + "children": [ + { + "tags": [], + "id": "def-server.ISavedObjectsPointInTimeFinder.find", + "type": "Function", + "label": "find", + "description": [ + "\nAn async generator which wraps calls to `savedObjectsClient.find` and\niterates over multiple pages of results using `_pit` and `search_after`.\nThis will open a new Point-In-Time (PIT), and continue paging until a set\nof results is received that's smaller than the designated `perPage` size." + ], + "source": { + "path": "src/core/server/saved_objects/service/lib/point_in_time_finder.ts", + "lineNumber": 49 + }, + "signature": [ + "() => AsyncGenerator<", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindResponse", + "text": "SavedObjectsFindResponse" + }, + ", any, unknown>" + ] + }, + { + "tags": [], + "id": "def-server.ISavedObjectsPointInTimeFinder.close", + "type": "Function", + "label": "close", + "description": [ + "\nCloses the Point-In-Time associated with this finder instance.\n\nOnce you have retrieved all of the results you need, it is recommended\nto call `close()` to clean up the PIT and prevent Elasticsearch from\nconsuming resources unnecessarily. This is only required if you are\ndone iterating and have not yet paged through all of the results: the\nPIT will automatically be closed for you once you reach the last page\nof results, or if the underlying call to `find` fails for any reason." + ], + "source": { + "path": "src/core/server/saved_objects/service/lib/point_in_time_finder.ts", + "lineNumber": 60 + }, + "signature": [ + "() => Promise" + ] + } + ], + "source": { + "path": "src/core/server/saved_objects/service/lib/point_in_time_finder.ts", + "lineNumber": 42 + }, + "initialIsOpen": false + }, { "id": "def-server.SavedObjectExportBaseOptions", "type": "Interface", @@ -7843,7 +8066,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 219 + "lineNumber": 224 }, "signature": [ "string | undefined" @@ -7859,7 +8082,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 221 + "lineNumber": 226 }, "signature": [ "boolean | \"wait_for\" | undefined" @@ -7868,7 +8091,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 217 + "lineNumber": 222 }, "initialIsOpen": false }, @@ -7893,7 +8116,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 230 + "lineNumber": 235 }, "signature": [ "string[]" @@ -7902,7 +8125,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 228 + "lineNumber": 233 }, "initialIsOpen": false }, @@ -7927,7 +8150,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 141 + "lineNumber": 142 }, "signature": [ "string | undefined" @@ -7936,7 +8159,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 139 + "lineNumber": 140 }, "initialIsOpen": false }, @@ -7969,7 +8192,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 66 + "lineNumber": 71 }, "signature": [ "string | undefined" @@ -7983,7 +8206,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 67 + "lineNumber": 72 } }, { @@ -7994,7 +8217,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 68 + "lineNumber": 73 }, "signature": [ "T" @@ -8008,7 +8231,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 69 + "lineNumber": 74 }, "signature": [ "string | undefined" @@ -8022,7 +8245,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 70 + "lineNumber": 75 }, "signature": [ { @@ -8045,7 +8268,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 72 + "lineNumber": 77 }, "signature": [ { @@ -8068,7 +8291,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 82 + "lineNumber": 87 }, "signature": [ "string | undefined" @@ -8084,7 +8307,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 84 + "lineNumber": 89 }, "signature": [ "string | undefined" @@ -8100,7 +8323,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 91 + "lineNumber": 96 }, "signature": [ "string[] | undefined" @@ -8109,7 +8332,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 65 + "lineNumber": 70 }, "initialIsOpen": false }, @@ -8132,7 +8355,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 294 + "lineNumber": 299 } }, { @@ -8143,7 +8366,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 295 + "lineNumber": 300 } }, { @@ -8156,7 +8379,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 297 + "lineNumber": 302 }, "signature": [ "string[] | undefined" @@ -8165,7 +8388,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 293 + "lineNumber": 298 }, "initialIsOpen": false }, @@ -8198,7 +8421,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 120 + "lineNumber": 125 }, "signature": [ { @@ -8214,7 +8437,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 119 + "lineNumber": 124 }, "initialIsOpen": false }, @@ -8247,7 +8470,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 305 + "lineNumber": 310 }, "signature": [ { @@ -8263,7 +8486,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 304 + "lineNumber": 309 }, "initialIsOpen": false }, @@ -8306,7 +8529,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 101 + "lineNumber": 106 } }, { @@ -8319,7 +8542,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 103 + "lineNumber": 108 } }, { @@ -8332,7 +8555,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 105 + "lineNumber": 110 }, "signature": [ "Partial" @@ -8348,7 +8571,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 112 + "lineNumber": 117 }, "signature": [ "string | undefined" @@ -8357,7 +8580,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 98 + "lineNumber": 103 }, "initialIsOpen": false }, @@ -8399,7 +8622,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 275 + "lineNumber": 280 }, "signature": [ "boolean | \"wait_for\" | undefined" @@ -8408,7 +8631,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 273 + "lineNumber": 278 }, "initialIsOpen": false }, @@ -8441,7 +8664,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 313 + "lineNumber": 318 }, "signature": [ { @@ -8457,7 +8680,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 312 + "lineNumber": 317 }, "initialIsOpen": false }, @@ -8480,7 +8703,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 184 + "lineNumber": 189 } }, { @@ -8491,13 +8714,13 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 185 + "lineNumber": 190 } } ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 183 + "lineNumber": 188 }, "initialIsOpen": false }, @@ -8520,7 +8743,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 193 + "lineNumber": 198 }, "signature": [ "{ id: string; type: string; error: ", @@ -8537,7 +8760,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 192 + "lineNumber": 197 }, "initialIsOpen": false }, @@ -8617,7 +8840,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { @@ -8689,7 +8912,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 385 + "lineNumber": 390 } }, { @@ -8702,13 +8925,13 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 389 + "lineNumber": 394 } } ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 380 + "lineNumber": 385 }, "initialIsOpen": false }, @@ -8931,7 +9154,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 27 + "lineNumber": 32 }, "signature": [ "string | undefined" @@ -8947,7 +9170,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 29 + "lineNumber": 34 }, "signature": [ "boolean | undefined" @@ -8963,7 +9186,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 34 + "lineNumber": 39 }, "signature": [ "string | undefined" @@ -8979,7 +9202,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 36 + "lineNumber": 41 }, "signature": [ { @@ -9002,7 +9225,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 46 + "lineNumber": 51 }, "signature": [ "string | undefined" @@ -9016,7 +9239,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 47 + "lineNumber": 52 }, "signature": [ { @@ -9039,7 +9262,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 49 + "lineNumber": 54 }, "signature": [ "boolean | \"wait_for\" | undefined" @@ -9055,7 +9278,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 51 + "lineNumber": 56 }, "signature": [ "string | undefined" @@ -9071,7 +9294,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 58 + "lineNumber": 63 }, "signature": [ "string[] | undefined" @@ -9080,7 +9303,45 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 25 + "lineNumber": 30 + }, + "initialIsOpen": false + }, + { + "id": "def-server.SavedObjectsCreatePointInTimeFinderDependencies", + "type": "Interface", + "label": "SavedObjectsCreatePointInTimeFinderDependencies", + "description": [], + "tags": [ + "public" + ], + "children": [ + { + "tags": [], + "id": "def-server.SavedObjectsCreatePointInTimeFinderDependencies.client", + "type": "Object", + "label": "client", + "description": [], + "source": { + "path": "src/core/server/saved_objects/service/lib/point_in_time_finder.ts", + "lineNumber": 30 + }, + "signature": [ + "Pick, \"find\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ] + } + ], + "source": { + "path": "src/core/server/saved_objects/service/lib/point_in_time_finder.ts", + "lineNumber": 29 }, "initialIsOpen": false }, @@ -9122,7 +9383,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 118 + "lineNumber": 133 }, "signature": [ "boolean | undefined" @@ -9131,7 +9392,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 116 + "lineNumber": 131 }, "initialIsOpen": false }, @@ -9173,7 +9434,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 239 + "lineNumber": 244 }, "signature": [ "boolean | \"wait_for\" | undefined" @@ -9182,7 +9443,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 237 + "lineNumber": 242 }, "initialIsOpen": false }, @@ -9207,7 +9468,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 248 + "lineNumber": 253 }, "signature": [ "string[]" @@ -9216,7 +9477,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 246 + "lineNumber": 251 }, "initialIsOpen": false }, @@ -9258,7 +9519,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 284 + "lineNumber": 289 }, "signature": [ "boolean | \"wait_for\" | undefined" @@ -9274,7 +9535,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 286 + "lineNumber": 291 }, "signature": [ "boolean | undefined" @@ -9283,7 +9544,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 282 + "lineNumber": 287 }, "initialIsOpen": false }, @@ -9548,7 +9809,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 78 + "lineNumber": 79 }, "signature": [ "string | string[]" @@ -9562,7 +9823,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 79 + "lineNumber": 80 }, "signature": [ "number | undefined" @@ -9576,7 +9837,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 80 + "lineNumber": 81 }, "signature": [ "number | undefined" @@ -9590,7 +9851,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 81 + "lineNumber": 82 }, "signature": [ "string | undefined" @@ -9599,15 +9860,15 @@ { "tags": [], "id": "def-server.SavedObjectsFindOptions.sortOrder", - "type": "string", + "type": "CompoundType", "label": "sortOrder", "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 82 + "lineNumber": 83 }, "signature": [ - "string | undefined" + "\"asc\" | \"desc\" | \"_doc\" | undefined" ] }, { @@ -9620,7 +9881,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 88 + "lineNumber": 89 }, "signature": [ "string[] | undefined" @@ -9636,7 +9897,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 90 + "lineNumber": 91 }, "signature": [ "string | undefined" @@ -9652,7 +9913,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 92 + "lineNumber": 93 }, "signature": [ "string[] | undefined" @@ -9668,10 +9929,10 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 96 + "lineNumber": 97 }, "signature": [ - "unknown[] | undefined" + "string[] | undefined" ] }, { @@ -9684,7 +9945,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 101 + "lineNumber": 102 }, "signature": [ "string[] | undefined" @@ -9700,7 +9961,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 107 + "lineNumber": 108 }, "signature": [ { @@ -9731,7 +9992,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 111 + "lineNumber": 112 }, "signature": [ "\"AND\" | \"OR\" | undefined" @@ -9747,7 +10008,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 116 + "lineNumber": 117 }, "signature": [ "\"AND\" | \"OR\" | undefined" @@ -9761,7 +10022,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 117 + "lineNumber": 118 }, "signature": [ "any" @@ -9775,7 +10036,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 118 + "lineNumber": 119 }, "signature": [ "string[] | undefined" @@ -9791,7 +10052,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 126 + "lineNumber": 127 }, "signature": [ "Map | undefined" @@ -9807,7 +10068,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 128 + "lineNumber": 129 }, "signature": [ "string | undefined" @@ -9823,7 +10084,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 132 + "lineNumber": 133 }, "signature": [ { @@ -9839,7 +10100,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 77 + "lineNumber": 78 }, "initialIsOpen": false }, @@ -9860,7 +10121,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 61 + "lineNumber": 62 } }, { @@ -9871,13 +10132,13 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 62 + "lineNumber": 63 } } ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 60 + "lineNumber": 61 }, "initialIsOpen": false }, @@ -9910,7 +10171,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 172 + "lineNumber": 177 }, "signature": [ { @@ -9931,7 +10192,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 173 + "lineNumber": 178 } }, { @@ -9942,7 +10203,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 174 + "lineNumber": 179 } }, { @@ -9953,7 +10214,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 175 + "lineNumber": 180 } }, { @@ -9964,7 +10225,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 176 + "lineNumber": 181 }, "signature": [ "string | undefined" @@ -9973,7 +10234,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 171 + "lineNumber": 176 }, "initialIsOpen": false }, @@ -10010,7 +10271,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 131 + "lineNumber": 136 } }, { @@ -10023,16 +10284,16 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 160 + "lineNumber": 165 }, "signature": [ - "unknown[] | undefined" + "string[] | undefined" ] } ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 127 + "lineNumber": 132 }, "initialIsOpen": false }, @@ -10971,7 +11232,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 135 + "lineNumber": 150 } }, { @@ -10984,7 +11245,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 137 + "lineNumber": 152 }, "signature": [ "number | undefined" @@ -10993,7 +11254,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 133 + "lineNumber": 148 }, "initialIsOpen": false }, @@ -11009,7 +11270,7 @@ "section": "def-server.SavedObjectsIncrementCounterOptions", "text": "SavedObjectsIncrementCounterOptions" }, - " extends ", + " extends ", { "pluginId": "core", "scope": "server", @@ -11033,7 +11294,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 102 + "lineNumber": 113 }, "signature": [ "boolean | undefined" @@ -11049,7 +11310,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 104 + "lineNumber": 115 }, "signature": [ { @@ -11072,16 +11333,32 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 109 + "lineNumber": 120 }, "signature": [ "boolean | \"wait_for\" | undefined" ] + }, + { + "tags": [], + "id": "def-server.SavedObjectsIncrementCounterOptions.upsertAttributes", + "type": "Uncategorized", + "label": "upsertAttributes", + "description": [ + "\nAttributes to use when upserting the document if it doesn't exist." + ], + "source": { + "path": "src/core/server/saved_objects/service/lib/repository.ts", + "lineNumber": 124 + }, + "signature": [ + "Attributes | undefined" + ] } ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 97 + "lineNumber": 107 }, "initialIsOpen": false }, @@ -11243,7 +11520,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 355 + "lineNumber": 360 }, "signature": [ "string | undefined" @@ -11259,7 +11536,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 359 + "lineNumber": 364 }, "signature": [ "string | undefined" @@ -11268,7 +11545,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 351 + "lineNumber": 356 }, "initialIsOpen": false }, @@ -11291,13 +11568,13 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 369 + "lineNumber": 374 } } ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 365 + "lineNumber": 370 }, "initialIsOpen": false }, @@ -11318,7 +11595,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 69 + "lineNumber": 70 } }, { @@ -11329,7 +11606,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 70 + "lineNumber": 71 }, "signature": [ "string | undefined" @@ -11338,7 +11615,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 68 + "lineNumber": 69 }, "initialIsOpen": false }, @@ -11491,7 +11768,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 257 + "lineNumber": 262 }, "signature": [ "boolean | undefined" @@ -11500,7 +11777,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 255 + "lineNumber": 260 }, "initialIsOpen": false }, @@ -11542,13 +11819,13 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 266 + "lineNumber": 271 } } ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 264 + "lineNumber": 269 }, "initialIsOpen": false }, @@ -11592,7 +11869,7 @@ "section": "def-server.SavedObjectsRepository", "text": "SavedObjectsRepository" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"deleteByNamespace\" | \"incrementCounter\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\" | \"deleteByNamespace\" | \"incrementCounter\">" ] }, { @@ -11616,7 +11893,7 @@ "section": "def-server.SavedObjectsRepository", "text": "SavedObjectsRepository" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"deleteByNamespace\" | \"incrementCounter\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\" | \"deleteByNamespace\" | \"incrementCounter\">" ] } ], @@ -11741,7 +12018,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 331 + "lineNumber": 336 }, "signature": [ { @@ -11764,7 +12041,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 341 + "lineNumber": 346 }, "signature": [ "\"conflict\" | \"exactMatch\" | \"aliasMatch\"" @@ -11780,7 +12057,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 345 + "lineNumber": 350 }, "signature": [ "string | undefined" @@ -11789,7 +12066,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 330 + "lineNumber": 335 }, "initialIsOpen": false }, @@ -11931,7 +12208,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { @@ -11963,7 +12240,7 @@ "section": "def-server.SavedObjectsRepository", "text": "SavedObjectsRepository" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"deleteByNamespace\" | \"incrementCounter\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\" | \"deleteByNamespace\" | \"incrementCounter\">" ] }, { @@ -11987,7 +12264,7 @@ "section": "def-server.SavedObjectsRepository", "text": "SavedObjectsRepository" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"deleteByNamespace\" | \"incrementCounter\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\" | \"deleteByNamespace\" | \"incrementCounter\">" ] }, { @@ -12034,7 +12311,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">) => Pick<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">) => Pick<", { "pluginId": "core", "scope": "server", @@ -12066,7 +12343,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">) => Pick<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">) => Pick<", { "pluginId": "core", "scope": "server", @@ -12127,7 +12404,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 50 + "lineNumber": 51 }, "signature": [ "{ [status: string]: number; skipped: number; migrated: number; }" @@ -12136,7 +12413,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 49 + "lineNumber": 50 }, "initialIsOpen": false }, @@ -12159,7 +12436,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 237 + "lineNumber": 238 } }, { @@ -12172,7 +12449,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 244 + "lineNumber": 245 } }, { @@ -12185,7 +12462,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 248 + "lineNumber": 249 }, "signature": [ { @@ -12207,7 +12484,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 252 + "lineNumber": 253 }, "signature": [ "string | undefined" @@ -12223,7 +12500,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 256 + "lineNumber": 257 }, "signature": [ "string | undefined" @@ -12239,7 +12516,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 260 + "lineNumber": 261 }, "signature": [ { @@ -12261,7 +12538,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 264 + "lineNumber": 265 }, "signature": [ { @@ -12292,7 +12569,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 313 + "lineNumber": 314 }, "signature": [ "string | undefined" @@ -12308,7 +12585,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 317 + "lineNumber": 318 }, "signature": [ { @@ -12324,7 +12601,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 233 + "lineNumber": 234 }, "initialIsOpen": false }, @@ -12349,7 +12626,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 329 + "lineNumber": 330 }, "signature": [ "boolean | undefined" @@ -12365,7 +12642,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 333 + "lineNumber": 334 }, "signature": [ "string | undefined" @@ -12381,7 +12658,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 338 + "lineNumber": 339 }, "signature": [ "string | undefined" @@ -12397,7 +12674,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 343 + "lineNumber": 344 }, "signature": [ "((savedObject: ", @@ -12421,7 +12698,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 348 + "lineNumber": 349 }, "signature": [ "((savedObject: ", @@ -12445,7 +12722,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 357 + "lineNumber": 358 }, "signature": [ "((savedObject: ", @@ -12469,7 +12746,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 368 + "lineNumber": 369 }, "signature": [ { @@ -12492,7 +12769,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 411 + "lineNumber": 412 }, "signature": [ { @@ -12508,7 +12785,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 325 + "lineNumber": 326 }, "initialIsOpen": false }, @@ -12606,7 +12883,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 206 + "lineNumber": 211 }, "signature": [ "string | undefined" @@ -12622,7 +12899,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 208 + "lineNumber": 213 }, "signature": [ { @@ -12645,7 +12922,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 210 + "lineNumber": 215 }, "signature": [ "boolean | \"wait_for\" | undefined" @@ -12654,7 +12931,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 204 + "lineNumber": 209 }, "initialIsOpen": false }, @@ -12689,7 +12966,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 322 + "lineNumber": 327 }, "signature": [ "Partial" @@ -12703,7 +12980,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 323 + "lineNumber": 328 }, "signature": [ { @@ -12719,7 +12996,7 @@ ], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 320 + "lineNumber": 325 }, "initialIsOpen": false } @@ -12736,7 +13013,7 @@ "description": [], "source": { "path": "src/core/server/saved_objects/export/saved_objects_exporter.ts", - "lineNumber": 32 + "lineNumber": 31 }, "signature": [ "{ exportByTypes: (options: SavedObjectsExportByTypeOptions) => Promise<", @@ -12776,10 +13053,10 @@ ], "source": { "path": "src/core/server/saved_objects/service/lib/repository.ts", - "lineNumber": 128 + "lineNumber": 143 }, "signature": [ - "{ get: (type: string, id: string, options?: SavedObjectsBaseOptions) => Promise>; delete: (type: string, id: string, options?: SavedObjectsDeleteOptions) => Promise<{}>; create: (type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise>; find: (options: SavedObjectsFindOptions) => Promise>; update: (type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions) => Promise>; bulkCreate: (objects: SavedObjectsBulkCreateObject[], options?: SavedObjectsCreateOptions) => Promise>; bulkGet: (objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions) => Promise>; bulkUpdate: (objects: SavedObjectsBulkUpdateObject[], options?: SavedObjectsBulkUpdateOptions) => Promise>; checkConflicts: (objects?: SavedObjectsCheckConflictsObject[], options?: SavedObjectsBaseOptions) => Promise; resolve: (type: string, id: string, options?: SavedObjectsBaseOptions) => Promise>; addToNamespaces: (type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions) => Promise; deleteFromNamespaces: (type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions) => Promise; removeReferencesTo: (type: string, id: string, options?: SavedObjectsRemoveReferencesToOptions) => Promise; openPointInTimeForType: (type: string | string[], { keepAlive, preference }?: SavedObjectsOpenPointInTimeOptions) => Promise; closePointInTime: (id: string, options?: SavedObjectsBaseOptions | undefined) => Promise; deleteByNamespace: (namespace: string, options?: SavedObjectsDeleteByNamespaceOptions) => Promise; incrementCounter: (type: string, id: string, counterFields: (string | SavedObjectsIncrementCounterField)[], options?: SavedObjectsIncrementCounterOptions) => Promise>; }" + "{ get: (type: string, id: string, options?: SavedObjectsBaseOptions) => Promise>; delete: (type: string, id: string, options?: SavedObjectsDeleteOptions) => Promise<{}>; create: (type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise>; find: (options: SavedObjectsFindOptions) => Promise>; update: (type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions) => Promise>; bulkCreate: (objects: SavedObjectsBulkCreateObject[], options?: SavedObjectsCreateOptions) => Promise>; bulkGet: (objects?: SavedObjectsBulkGetObject[], options?: SavedObjectsBaseOptions) => Promise>; bulkUpdate: (objects: SavedObjectsBulkUpdateObject[], options?: SavedObjectsBulkUpdateOptions) => Promise>; checkConflicts: (objects?: SavedObjectsCheckConflictsObject[], options?: SavedObjectsBaseOptions) => Promise; resolve: (type: string, id: string, options?: SavedObjectsBaseOptions) => Promise>; addToNamespaces: (type: string, id: string, namespaces: string[], options?: SavedObjectsAddToNamespacesOptions) => Promise; deleteFromNamespaces: (type: string, id: string, namespaces: string[], options?: SavedObjectsDeleteFromNamespacesOptions) => Promise; removeReferencesTo: (type: string, id: string, options?: SavedObjectsRemoveReferencesToOptions) => Promise; openPointInTimeForType: (type: string | string[], { keepAlive, preference }?: SavedObjectsOpenPointInTimeOptions) => Promise; closePointInTime: (id: string, options?: SavedObjectsBaseOptions | undefined) => Promise; createPointInTimeFinder: (findOptions: Pick, dependencies?: SavedObjectsCreatePointInTimeFinderDependencies | undefined) => ISavedObjectsPointInTimeFinder; deleteByNamespace: (namespace: string, options?: SavedObjectsDeleteByNamespaceOptions) => Promise; incrementCounter: (type: string, id: string, counterFields: (string | SavedObjectsIncrementCounterField)[], options?: SavedObjectsIncrementCounterOptions) => Promise>; }" ], "initialIsOpen": false }, @@ -12814,7 +13091,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 148 + "lineNumber": 149 }, "signature": [ "false | true | \"wait_for\"" @@ -12895,7 +13172,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 212 + "lineNumber": 213 }, "signature": [ "{ get: (type: string, id: string, options?: SavedObjectsBaseOptions) => Promise>; delete: (type: string, id: string, options?: ", @@ -12972,7 +13249,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ], "initialIsOpen": false }, @@ -13041,7 +13318,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ], "initialIsOpen": false }, @@ -13055,13 +13332,46 @@ "description": [], "source": { "path": "src/core/server/saved_objects/service/saved_objects_client.ts", - "lineNumber": 375 + "lineNumber": 380 }, "signature": [ "SavedObjectsBaseOptions" ], "initialIsOpen": false }, + { + "id": "def-server.SavedObjectsCreatePointInTimeFinderOptions", + "type": "Type", + "label": "SavedObjectsCreatePointInTimeFinderOptions", + "tags": [ + "public" + ], + "description": [], + "source": { + "path": "src/core/server/saved_objects/service/lib/point_in_time_finder.ts", + "lineNumber": 21 + }, + "signature": [ + "{ type: string | string[]; filter?: any; fields?: string[] | undefined; search?: string | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: \"asc\" | \"desc\" | \"_doc\" | undefined; searchFields?: string[] | undefined; rootSearchFields?: string[] | undefined; hasReference?: ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + " | ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreSavedObjectsPluginApi", + "section": "def-server.SavedObjectsFindOptionsReference", + "text": "SavedObjectsFindOptionsReference" + }, + "[] | undefined; hasReferenceOperator?: \"AND\" | \"OR\" | undefined; defaultSearchOperator?: \"AND\" | \"OR\" | undefined; namespaces?: string[] | undefined; typeToNamespacesMap?: Map | undefined; preference?: string | undefined; }" + ], + "initialIsOpen": false + }, { "id": "def-server.SavedObjectsExportTransform", "type": "Type", @@ -13210,7 +13520,7 @@ ], "source": { "path": "src/core/server/saved_objects/types.ts", - "lineNumber": 226 + "lineNumber": 227 }, "signature": [ "\"multiple\" | \"single\" | \"multiple-isolated\" | \"agnostic\"" diff --git a/api_docs/data.json b/api_docs/data.json index a9ef03d881ce8..a51ad465fe903 100644 --- a/api_docs/data.json +++ b/api_docs/data.json @@ -7022,8 +7022,7 @@ "docId": "kibDataSearchPluginApi", "section": "def-common.ISearchRequestParams", "text": "ISearchRequestParams" - }, - ">" + } ], "description": [], "children": [ @@ -8696,7 +8695,7 @@ "section": "def-common.ISearchRequestParams", "text": "ISearchRequestParams" }, - ">>" + ">" ], "description": [], "tags": [], @@ -8709,7 +8708,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 20 + "lineNumber": 19 }, "signature": [ "string | undefined" @@ -8718,7 +8717,7 @@ ], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 19 + "lineNumber": 18 }, "initialIsOpen": false }, @@ -10763,7 +10762,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 37 + "lineNumber": 40 } }, { @@ -10774,7 +10773,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 38 + "lineNumber": 41 } }, { @@ -10785,7 +10784,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 39 + "lineNumber": 42 } }, { @@ -10796,7 +10795,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 40 + "lineNumber": 43 } }, { @@ -10807,7 +10806,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 41 + "lineNumber": 44 } }, { @@ -10818,13 +10817,13 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 42 + "lineNumber": 45 } } ], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 36 + "lineNumber": 39 }, "initialIsOpen": false }, @@ -11484,7 +11483,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 13 + "lineNumber": 12 }, "signature": [ "\"es\"" @@ -11608,7 +11607,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 26 + "lineNumber": 34 }, "signature": [ "ExpressionFunctionDefinition<\"kibana_context\", ", @@ -11751,10 +11750,10 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 23 + "lineNumber": 22 }, "signature": [ - "IKibanaSearchResponse>" + "IKibanaSearchResponse>" ], "initialIsOpen": false }, @@ -11847,7 +11846,7 @@ "description": [], "source": { "path": "src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts", - "lineNumber": 34 + "lineNumber": 35 }, "signature": [ "ExpressionFunctionDefinition<\"indexPatternLoad\", null, Arguments, Output, ", @@ -14487,15 +14486,7 @@ "lineNumber": 46 }, "signature": [ - "{ addQuerySuggestionProvider: (language: string, provider: ", - { - "pluginId": "data", - "scope": "public", - "docId": "kibDataAutocompletePluginApi", - "section": "def-public.QuerySuggestionGetFn", - "text": "QuerySuggestionGetFn" - }, - ") => void; getQuerySuggestions: ", + "{ getQuerySuggestions: ", { "pluginId": "data", "scope": "public", @@ -15163,7 +15154,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, elasticsearchClient: ", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, elasticsearchClient: ", { "pluginId": "core", "scope": "server", @@ -15190,7 +15181,7 @@ "description": [], "source": { "path": "src/plugins/data/server/plugin.ts", - "lineNumber": 107 + "lineNumber": 104 } } ], @@ -15198,7 +15189,7 @@ "returnComment": [], "source": { "path": "src/plugins/data/server/plugin.ts", - "lineNumber": 107 + "lineNumber": 104 } }, { @@ -15214,7 +15205,7 @@ "returnComment": [], "source": { "path": "src/plugins/data/server/plugin.ts", - "lineNumber": 121 + "lineNumber": 118 } } ], @@ -19507,7 +19498,7 @@ "section": "def-common.ISearchRequestParams", "text": "ISearchRequestParams" }, - ">>" + ">" ], "description": [], "tags": [], @@ -19520,7 +19511,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 20 + "lineNumber": 19 }, "signature": [ "string | undefined" @@ -19529,7 +19520,7 @@ ], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 19 + "lineNumber": 18 }, "initialIsOpen": false }, @@ -20433,7 +20424,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 13 + "lineNumber": 12 }, "signature": [ "\"es\"" @@ -20527,7 +20518,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 26 + "lineNumber": 34 }, "signature": [ "ExpressionFunctionDefinition<\"kibana_context\", ", @@ -20636,10 +20627,10 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 23 + "lineNumber": 22 }, "signature": [ - "IKibanaSearchResponse>" + "IKibanaSearchResponse>" ], "initialIsOpen": false }, @@ -20717,7 +20708,7 @@ "description": [], "source": { "path": "src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts", - "lineNumber": 34 + "lineNumber": 35 }, "signature": [ "ExpressionFunctionDefinition<\"indexPatternLoad\", null, Arguments, Output, ", diff --git a/api_docs/data_autocomplete.json b/api_docs/data_autocomplete.json index a66ca49d0c181..7792a7abe5c8a 100644 --- a/api_docs/data_autocomplete.json +++ b/api_docs/data_autocomplete.json @@ -324,7 +324,7 @@ "description": [], "source": { "path": "src/plugins/data/public/autocomplete/autocomplete_service.ts", - "lineNumber": 93 + "lineNumber": 96 }, "signature": [ "{ getQuerySuggestions: QuerySuggestionGetFn; hasQuerySuggestions: (language: string) => boolean; getValueSuggestions: ValueSuggestionsGetFn; }" diff --git a/api_docs/data_enhanced.json b/api_docs/data_enhanced.json index 5bd7a970f9b73..80f1d1fc15a5a 100644 --- a/api_docs/data_enhanced.json +++ b/api_docs/data_enhanced.json @@ -46,7 +46,7 @@ "description": [], "source": { "path": "x-pack/plugins/data_enhanced/public/plugin.ts", - "lineNumber": 40 + "lineNumber": 38 }, "signature": [ "void" @@ -903,9 +903,7 @@ "lineNumber": 27 }, "signature": [ - "IKibanaSearchResponse>" + "IKibanaSearchResponse>" ], "initialIsOpen": false }, diff --git a/api_docs/data_index_patterns.json b/api_docs/data_index_patterns.json index afcea7d50b304..8058f6a72f9c3 100644 --- a/api_docs/data_index_patterns.json +++ b/api_docs/data_index_patterns.json @@ -368,7 +368,7 @@ "section": "def-server.DataPluginStart", "text": "DataPluginStart" }, - ">, { logger, expressions }: ", + ">, { expressions }: ", { "pluginId": "data", "scope": "server", @@ -413,12 +413,12 @@ "description": [], "source": { "path": "src/plugins/data/server/index_patterns/index_patterns_service.ts", - "lineNumber": 49 + "lineNumber": 47 } }, { "type": "Object", - "label": "{ logger, expressions }", + "label": "{ expressions }", "isRequired": true, "signature": [ { @@ -432,7 +432,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index_patterns/index_patterns_service.ts", - "lineNumber": 50 + "lineNumber": 48 } } ], @@ -440,7 +440,7 @@ "returnComment": [], "source": { "path": "src/plugins/data/server/index_patterns/index_patterns_service.ts", - "lineNumber": 48 + "lineNumber": 46 } }, { @@ -472,7 +472,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, elasticsearchClient: ", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, elasticsearchClient: ", { "pluginId": "core", "scope": "server", @@ -507,7 +507,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index_patterns/index_patterns_service.ts", - "lineNumber": 76 + "lineNumber": 58 } }, { @@ -526,7 +526,7 @@ "description": [], "source": { "path": "src/plugins/data/server/index_patterns/index_patterns_service.ts", - "lineNumber": 76 + "lineNumber": 58 } } ], @@ -534,13 +534,13 @@ "returnComment": [], "source": { "path": "src/plugins/data/server/index_patterns/index_patterns_service.ts", - "lineNumber": 76 + "lineNumber": 58 } } ], "source": { "path": "src/plugins/data/server/index_patterns/index_patterns_service.ts", - "lineNumber": 47 + "lineNumber": 45 }, "initialIsOpen": false } @@ -3903,7 +3903,7 @@ "label": "getIndexPatternLoadMeta", "source": { "path": "src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts", - "lineNumber": 41 + "lineNumber": 42 }, "tags": [], "returnComment": [], @@ -5937,7 +5937,7 @@ "description": [], "source": { "path": "src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts", - "lineNumber": 18 + "lineNumber": 19 }, "signature": [ "\"index_pattern\"" @@ -5951,7 +5951,7 @@ "description": [], "source": { "path": "src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts", - "lineNumber": 19 + "lineNumber": 20 }, "signature": [ { @@ -5966,7 +5966,7 @@ ], "source": { "path": "src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts", - "lineNumber": 17 + "lineNumber": 18 }, "initialIsOpen": false }, @@ -6677,7 +6677,7 @@ "description": [], "source": { "path": "src/plugins/data/common/index_patterns/expressions/load_index_pattern.ts", - "lineNumber": 34 + "lineNumber": 35 }, "signature": [ "ExpressionFunctionDefinition<\"indexPatternLoad\", null, Arguments, Output, ", diff --git a/api_docs/data_search.json b/api_docs/data_search.json index a75b669cbd288..53b8294de02fc 100644 --- a/api_docs/data_search.json +++ b/api_docs/data_search.json @@ -1621,7 +1621,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 88 + "lineNumber": 90 }, "signature": [ "(sessionId: string, attributes: Partial) => Promise<", @@ -1643,7 +1643,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 89 + "lineNumber": 91 }, "signature": [ "(sessionId: string) => Promise<", @@ -1665,7 +1665,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 90 + "lineNumber": 92 }, "signature": [ "(options: Pick<", @@ -1695,7 +1695,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 91 + "lineNumber": 93 }, "signature": [ "(sessionId: string, attributes: Partial) => Promise<", @@ -1717,7 +1717,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 92 + "lineNumber": 94 }, "signature": [ "(sessionId: string) => Promise<{}>" @@ -1731,7 +1731,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 93 + "lineNumber": 95 }, "signature": [ "(sessionId: string) => Promise<{}>" @@ -1745,7 +1745,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 94 + "lineNumber": 96 }, "signature": [ "(sessionId: string, expires: Date) => Promise<", @@ -1762,7 +1762,7 @@ ], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 87 + "lineNumber": 89 }, "initialIsOpen": false }, @@ -1843,7 +1843,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 41 + "lineNumber": 43 }, "signature": [ { @@ -1865,7 +1865,7 @@ ], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 46 + "lineNumber": 48 }, "signature": [ " ", @@ -2009,7 +2009,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 110 + "lineNumber": 112 }, "signature": [ "(request: ", @@ -2038,7 +2038,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 111 + "lineNumber": 113 }, "signature": [ "{ asScoped: (request: ", @@ -2063,7 +2063,7 @@ ], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 97 + "lineNumber": 99 }, "initialIsOpen": false }, @@ -2094,7 +2094,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 73 + "lineNumber": 75 }, "signature": [ "(request: SearchStrategyRequest, options: ", @@ -2126,7 +2126,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 78 + "lineNumber": 80 }, "signature": [ "((id: string, options: ", @@ -2156,7 +2156,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 79 + "lineNumber": 81 }, "signature": [ "((id: string, keepAlive: string, options: ", @@ -2181,7 +2181,7 @@ ], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 69 + "lineNumber": 71 }, "initialIsOpen": false }, @@ -2200,7 +2200,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 34 + "lineNumber": 36 }, "signature": [ "Pick<", @@ -2211,7 +2211,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { @@ -2222,7 +2222,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 35 + "lineNumber": 37 }, "signature": [ { @@ -2242,7 +2242,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 36 + "lineNumber": 38 }, "signature": [ { @@ -2262,7 +2262,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 37 + "lineNumber": 39 }, "signature": [ { @@ -2278,7 +2278,7 @@ ], "source": { "path": "src/plugins/data/server/search/types.ts", - "lineNumber": 33 + "lineNumber": 35 }, "initialIsOpen": false }, @@ -2344,7 +2344,23 @@ } ], "enums": [], - "misc": [], + "misc": [ + { + "id": "def-server.SearchRequestHandlerContext", + "type": "Type", + "label": "SearchRequestHandlerContext", + "tags": [], + "description": [], + "source": { + "path": "src/plugins/data/server/search/types.ts", + "lineNumber": 118 + }, + "signature": [ + "IScopedSearchClient" + ], + "initialIsOpen": false + } + ], "objects": [] }, "common": { @@ -9455,6 +9471,70 @@ "returnComment": [], "initialIsOpen": false }, + { + "id": "def-common.getKibanaContextFn", + "type": "Function", + "children": [ + { + "type": "Function", + "label": "getStartDependencies", + "isRequired": true, + "signature": [ + "(getKibanaRequest: (() => ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" + }, + ") | undefined) => Promise<", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.KibanaContextStartDependencies", + "text": "KibanaContextStartDependencies" + }, + ">" + ], + "description": [], + "source": { + "path": "src/plugins/data/common/search/expressions/kibana_context.ts", + "lineNumber": 52 + } + } + ], + "signature": [ + "(getStartDependencies: (getKibanaRequest: (() => ", + { + "pluginId": "core", + "scope": "server", + "docId": "kibCoreHttpPluginApi", + "section": "def-server.KibanaRequest", + "text": "KibanaRequest" + }, + ") | undefined) => Promise<", + "KibanaContextStartDependencies", + ">) => ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ExpressionFunctionKibanaContext", + "text": "ExpressionFunctionKibanaContext" + } + ], + "description": [], + "label": "getKibanaContextFn", + "source": { + "path": "src/plugins/data/common/search/expressions/kibana_context.ts", + "lineNumber": 51 + }, + "tags": [], + "returnComment": [], + "initialIsOpen": false + }, { "id": "def-common.getMaxMetricAgg", "type": "Function", @@ -10015,8 +10095,7 @@ "docId": "kibDataSearchPluginApi", "section": "def-common.ISearchRequestParams", "text": "ISearchRequestParams" - }, - ">" + } ], "description": [], "children": [ @@ -15167,7 +15246,7 @@ ], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 33 + "lineNumber": 36 }, "signature": [ { @@ -15477,7 +15556,7 @@ "section": "def-common.ISearchRequestParams", "text": "ISearchRequestParams" }, - ">>" + ">" ], "description": [], "tags": [], @@ -15490,7 +15569,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 20 + "lineNumber": 19 }, "signature": [ "string | undefined" @@ -15499,7 +15578,7 @@ ], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 19 + "lineNumber": 18 }, "initialIsOpen": false }, @@ -16090,7 +16169,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 36 + "lineNumber": 35 }, "signature": [ "(params: { body: ", @@ -16120,7 +16199,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 40 + "lineNumber": 39 }, "signature": [ "BehaviorSubject", @@ -16130,7 +16209,7 @@ ], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 35 + "lineNumber": 34 }, "initialIsOpen": false }, @@ -16224,7 +16303,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 26 + "lineNumber": 25 }, "signature": [ "MsearchRequest[]" @@ -16233,7 +16312,7 @@ ], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 25 + "lineNumber": 24 }, "initialIsOpen": false }, @@ -16252,21 +16331,19 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 31 + "lineNumber": 30 }, "signature": [ "ApiResponse", "<{ responses: ", "SearchResponse", - "[]; }, ", - "Context", - ">" + "[]; }, unknown>" ] } ], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 30 + "lineNumber": 29 }, "initialIsOpen": false }, @@ -16613,7 +16690,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 37 + "lineNumber": 40 } }, { @@ -16624,7 +16701,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 38 + "lineNumber": 41 } }, { @@ -16635,7 +16712,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 39 + "lineNumber": 42 } }, { @@ -16646,7 +16723,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 40 + "lineNumber": 43 } }, { @@ -16657,7 +16734,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 41 + "lineNumber": 44 } }, { @@ -16668,13 +16745,13 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 42 + "lineNumber": 45 } } ], "source": { "path": "src/plugins/data/common/search/search_source/fetch/types.ts", - "lineNumber": 36 + "lineNumber": 39 }, "initialIsOpen": false }, @@ -17568,7 +17645,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 49 + "lineNumber": 48 } }, { @@ -17579,7 +17656,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 50 + "lineNumber": 49 }, "signature": [ "(params: ", @@ -17604,7 +17681,7 @@ ], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 48 + "lineNumber": 47 }, "initialIsOpen": false }, @@ -17633,7 +17710,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 54 + "lineNumber": 53 }, "signature": [ "Promise<", @@ -17649,7 +17726,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 55 + "lineNumber": 54 }, "signature": [ "() => void" @@ -17658,7 +17735,7 @@ ], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 53 + "lineNumber": 52 }, "initialIsOpen": false }, @@ -17694,7 +17771,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 44 + "lineNumber": 43 }, "signature": [ "Record[]" @@ -17703,7 +17780,7 @@ ], "source": { "path": "src/plugins/data/common/search/search_source/legacy/types.ts", - "lineNumber": 43 + "lineNumber": 42 }, "initialIsOpen": false }, @@ -18653,7 +18730,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 13 + "lineNumber": 12 }, "signature": [ "\"es\"" @@ -18877,7 +18954,7 @@ "description": [], "source": { "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 26 + "lineNumber": 34 }, "signature": [ "ExpressionFunctionDefinition<\"kibana_context\", ", @@ -19315,10 +19392,10 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 23 + "lineNumber": 22 }, "signature": [ - "IKibanaSearchResponse>" + "IKibanaSearchResponse>" ], "initialIsOpen": false }, @@ -19530,10 +19607,10 @@ "description": [], "source": { "path": "src/plugins/data/common/search/es_search/types.ts", - "lineNumber": 15 + "lineNumber": 14 }, "signature": [ - "{ trackTotalHits?: boolean | undefined; } & Search" + "{ trackTotalHits?: boolean | undefined; } & estypes.SearchRequest" ], "initialIsOpen": false }, @@ -20649,426 +20726,6 @@ }, "initialIsOpen": false }, - { - "id": "def-common.kibanaContextFunction", - "type": "Object", - "tags": [], - "children": [ - { - "tags": [], - "id": "def-common.kibanaContextFunction.name", - "type": "string", - "label": "name", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 44 - }, - "signature": [ - "\"kibana_context\"" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.type", - "type": "string", - "label": "type", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 45 - }, - "signature": [ - "\"kibana_context\"" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.inputTypes", - "type": "Array", - "label": "inputTypes", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 46 - }, - "signature": [ - "(\"kibana_context\" | \"null\")[]" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.help", - "type": "string", - "label": "help", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 47 - } - }, - { - "id": "def-common.kibanaContextFunction.args", - "type": "Object", - "tags": [], - "children": [ - { - "id": "def-common.kibanaContextFunction.args.q", - "type": "Object", - "tags": [], - "children": [ - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.q.types", - "type": "Array", - "label": "types", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 52 - }, - "signature": [ - "(\"null\" | \"kibana_query\")[]" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.q.aliases", - "type": "Array", - "label": "aliases", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 53 - }, - "signature": [ - "string[]" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.q.default", - "type": "Uncategorized", - "label": "default", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 54 - }, - "signature": [ - "null" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.q.help", - "type": "string", - "label": "help", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 55 - } - } - ], - "description": [], - "label": "q", - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 51 - } - }, - { - "id": "def-common.kibanaContextFunction.args.filters", - "type": "Object", - "tags": [], - "children": [ - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.filters.types", - "type": "Array", - "label": "types", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 60 - }, - "signature": [ - "(\"null\" | \"kibana_filter\")[]" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.filters.multi", - "type": "boolean", - "label": "multi", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 61 - }, - "signature": [ - "true" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.filters.help", - "type": "string", - "label": "help", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 62 - } - } - ], - "description": [], - "label": "filters", - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 59 - } - }, - { - "id": "def-common.kibanaContextFunction.args.timeRange", - "type": "Object", - "tags": [], - "children": [ - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.timeRange.types", - "type": "Array", - "label": "types", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 67 - }, - "signature": [ - "(\"null\" | \"timerange\")[]" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.timeRange.default", - "type": "Uncategorized", - "label": "default", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 68 - }, - "signature": [ - "null" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.timeRange.help", - "type": "string", - "label": "help", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 69 - } - } - ], - "description": [], - "label": "timeRange", - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 66 - } - }, - { - "id": "def-common.kibanaContextFunction.args.savedSearchId", - "type": "Object", - "tags": [], - "children": [ - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.savedSearchId.types", - "type": "Array", - "label": "types", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 74 - }, - "signature": [ - "(\"string\" | \"null\")[]" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.savedSearchId.default", - "type": "Uncategorized", - "label": "default", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 75 - }, - "signature": [ - "null" - ] - }, - { - "tags": [], - "id": "def-common.kibanaContextFunction.args.savedSearchId.help", - "type": "string", - "label": "help", - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 76 - } - } - ], - "description": [], - "label": "savedSearchId", - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 73 - } - } - ], - "description": [], - "label": "args", - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 50 - } - }, - { - "id": "def-common.kibanaContextFunction.fn", - "type": "Function", - "label": "fn", - "signature": [ - "(input: Input, args: Arguments, { getSavedObject }: ", - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExecutionContext", - "text": "ExecutionContext" - }, - "<", - { - "pluginId": "inspector", - "scope": "common", - "docId": "kibInspectorPluginApi", - "section": "def-common.Adapters", - "text": "Adapters" - }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, - ">) => Promise<{ type: \"kibana_context\"; query: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataQueryPluginApi", - "section": "def-common.Query", - "text": "Query" - }, - "[]; filters: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataPluginApi", - "section": "def-common.Filter", - "text": "Filter" - } - ], - "description": [], - "children": [ - { - "type": "CompoundType", - "label": "input", - "isRequired": false, - "signature": [ - "Input" - ], - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 82 - } - }, - { - "type": "Object", - "label": "args", - "isRequired": true, - "signature": [ - "Arguments" - ], - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 82 - } - }, - { - "type": "Object", - "label": "{ getSavedObject }", - "isRequired": true, - "signature": [ - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExecutionContext", - "text": "ExecutionContext" - }, - "<", - { - "pluginId": "inspector", - "scope": "common", - "docId": "kibInspectorPluginApi", - "section": "def-common.Adapters", - "text": "Adapters" - }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, - ">" - ], - "description": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 82 - } - } - ], - "tags": [], - "returnComment": [], - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 82 - } - } - ], - "description": [], - "label": "kibanaContextFunction", - "source": { - "path": "src/plugins/data/common/search/expressions/kibana_context.ts", - "lineNumber": 43 - }, - "initialIsOpen": false - }, { "id": "def-common.kibanaFilterFunction", "type": "Object", diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 70cbd23125248..370bd2ffd101e 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -36,6 +36,9 @@ import dataSearchObj from './data_search.json'; ### Interfaces +### Consts, variables and types + + ## Common ### Objects diff --git a/api_docs/event_log.json b/api_docs/event_log.json index 64266b0563556..aea4d0c001913 100644 --- a/api_docs/event_log.json +++ b/api_docs/event_log.json @@ -225,7 +225,7 @@ "description": [], "source": { "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "lineNumber": 35 + "lineNumber": 36 } }, { @@ -236,7 +236,7 @@ "description": [], "source": { "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "lineNumber": 36 + "lineNumber": 37 } }, { @@ -247,7 +247,7 @@ "description": [], "source": { "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "lineNumber": 37 + "lineNumber": 38 } }, { @@ -258,7 +258,7 @@ "description": [], "source": { "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "lineNumber": 38 + "lineNumber": 39 }, "signature": [ "(Readonly<{ '@timestamp'?: string | undefined; kibana?: Readonly<{ server_uuid?: string | undefined; alerting?: Readonly<{ status?: string | undefined; instance_id?: string | undefined; action_group_id?: string | undefined; action_subgroup?: string | undefined; } & {}> | undefined; saved_objects?: Readonly<{ type?: string | undefined; id?: string | undefined; rel?: string | undefined; namespace?: string | undefined; } & {}>[] | undefined; } & {}> | undefined; user?: Readonly<{ name?: string | undefined; } & {}> | undefined; error?: Readonly<{ message?: string | undefined; } & {}> | undefined; message?: string | undefined; tags?: string[] | undefined; ecs?: Readonly<{ version?: string | undefined; } & {}> | undefined; event?: Readonly<{ start?: string | undefined; end?: string | undefined; action?: string | undefined; provider?: string | undefined; duration?: number | undefined; outcome?: string | undefined; reason?: string | undefined; } & {}> | undefined; } & {}> | undefined)[]" @@ -267,7 +267,7 @@ ], "source": { "path": "x-pack/plugins/event_log/server/es/cluster_client_adapter.ts", - "lineNumber": 34 + "lineNumber": 35 }, "initialIsOpen": false } diff --git a/api_docs/expressions.json b/api_docs/expressions.json index eefffb009be2a..06c97e497ae41 100644 --- a/api_docs/expressions.json +++ b/api_docs/expressions.json @@ -1562,7 +1562,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 223 + "lineNumber": 230 } } ], @@ -1570,7 +1570,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 223 + "lineNumber": 230 } }, { @@ -1606,7 +1606,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } }, { @@ -1619,7 +1619,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } } ], @@ -1627,7 +1627,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } }, { @@ -1670,7 +1670,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } }, { @@ -1683,7 +1683,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } } ], @@ -1691,7 +1691,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } }, { @@ -1715,7 +1715,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 250 + "lineNumber": 258 } } ], @@ -2975,7 +2975,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 40 + "lineNumber": 35 } } ], @@ -2983,7 +2983,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 40 + "lineNumber": 35 } }, { @@ -3028,7 +3028,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 56 + "lineNumber": 45 } } ], @@ -3036,7 +3036,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 56 + "lineNumber": 45 } }, { @@ -3079,7 +3079,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 70 + "lineNumber": 59 } } ], @@ -3087,7 +3087,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 70 + "lineNumber": 59 } }, { @@ -3103,13 +3103,13 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 86 + "lineNumber": 75 } } ], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 37 + "lineNumber": 32 }, "initialIsOpen": false }, @@ -3180,7 +3180,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 40 + "lineNumber": 35 } } ], @@ -3188,7 +3188,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 40 + "lineNumber": 35 } }, { @@ -3233,7 +3233,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 56 + "lineNumber": 45 } } ], @@ -3241,7 +3241,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 56 + "lineNumber": 45 } }, { @@ -3284,7 +3284,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 70 + "lineNumber": 59 } } ], @@ -3292,7 +3292,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 70 + "lineNumber": 59 } }, { @@ -3308,13 +3308,13 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 86 + "lineNumber": 75 } } ], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 37 + "lineNumber": 32 }, "initialIsOpen": false }, @@ -5908,7 +5908,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 28 + "lineNumber": 27 }, "signature": [ "() => ExecutionContextSearch" @@ -5924,7 +5924,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 33 + "lineNumber": 32 }, "signature": [ "Record" @@ -5940,7 +5940,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 38 + "lineNumber": 37 }, "signature": [ "Record string | undefined" @@ -6012,7 +6012,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 60 + "lineNumber": 59 }, "signature": [ "(() => ", @@ -6026,46 +6026,6 @@ ") | undefined" ] }, - { - "tags": [], - "id": "def-public.ExecutionContext.getSavedObject", - "type": "Function", - "label": "getSavedObject", - "description": [ - "\nAllows to fetch saved objects from ElasticSearch. In browser `getSavedObject`\nfunction is provided automatically by the Expressions plugin. On the server\nthe caller of the expression has to provide this context function. The\nreason is because on the browser we always know the user who tries to\nfetch a saved object, thus saved object client is scoped automatically to\nthat user. However, on the server we can scope that saved object client to\nany user, or even not scope it at all and execute it as an \"internal\" user." - ], - "source": { - "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 71 - }, - "signature": [ - "((type: string, id: string) => Promise<", - { - "pluginId": "core", - "scope": "common", - "docId": "kibCorePluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" - }, - ">) | undefined" - ] - }, { "tags": [], "id": "def-public.ExecutionContext.isSyncColorsEnabled", @@ -6076,7 +6036,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 79 + "lineNumber": 64 }, "signature": [ "(() => boolean) | undefined" @@ -6085,7 +6045,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 21 + "lineNumber": 20 }, "initialIsOpen": false }, @@ -9481,7 +9441,7 @@ ], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 24 + "lineNumber": 19 }, "signature": [ "{ readonly getType: (name: string) => ", @@ -9562,7 +9522,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 30 + "lineNumber": 25 }, "signature": [ "typeof ", @@ -9583,7 +9543,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 31 + "lineNumber": 26 }, "signature": [ "typeof ", @@ -9604,7 +9564,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 32 + "lineNumber": 27 }, "signature": [ { @@ -9624,7 +9584,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 33 + "lineNumber": 28 }, "signature": [ "({ className, dataAttrs, padding, renderError, expression, onEvent, onData$, reload$, debounce, ...expressionLoaderOptions }: ", @@ -9646,7 +9606,7 @@ "description": [], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 34 + "lineNumber": 29 }, "signature": [ "typeof ", @@ -9662,7 +9622,7 @@ ], "source": { "path": "src/plugins/expressions/public/plugin.ts", - "lineNumber": 29 + "lineNumber": 24 }, "lifecycle": "start", "initialIsOpen": true @@ -11035,7 +10995,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 223 + "lineNumber": 230 } } ], @@ -11043,7 +11003,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 223 + "lineNumber": 230 } }, { @@ -11079,7 +11039,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } }, { @@ -11092,7 +11052,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } } ], @@ -11100,7 +11060,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } }, { @@ -11143,7 +11103,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } }, { @@ -11156,7 +11116,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } } ], @@ -11164,7 +11124,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } }, { @@ -11188,7 +11148,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 250 + "lineNumber": 258 } } ], @@ -13984,7 +13944,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 28 + "lineNumber": 27 }, "signature": [ "() => ExecutionContextSearch" @@ -14000,7 +13960,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 33 + "lineNumber": 32 }, "signature": [ "Record" @@ -14016,7 +13976,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 38 + "lineNumber": 37 }, "signature": [ "Record string | undefined" @@ -14088,7 +14048,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 60 + "lineNumber": 59 }, "signature": [ "(() => ", @@ -14102,46 +14062,6 @@ ") | undefined" ] }, - { - "tags": [], - "id": "def-server.ExecutionContext.getSavedObject", - "type": "Function", - "label": "getSavedObject", - "description": [ - "\nAllows to fetch saved objects from ElasticSearch. In browser `getSavedObject`\nfunction is provided automatically by the Expressions plugin. On the server\nthe caller of the expression has to provide this context function. The\nreason is because on the browser we always know the user who tries to\nfetch a saved object, thus saved object client is scoped automatically to\nthat user. However, on the server we can scope that saved object client to\nany user, or even not scope it at all and execute it as an \"internal\" user." - ], - "source": { - "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 71 - }, - "signature": [ - "((type: string, id: string) => Promise<", - { - "pluginId": "core", - "scope": "common", - "docId": "kibCorePluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" - }, - ">) | undefined" - ] - }, { "tags": [], "id": "def-server.ExecutionContext.isSyncColorsEnabled", @@ -14152,7 +14072,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 79 + "lineNumber": 64 }, "signature": [ "(() => boolean) | undefined" @@ -14161,7 +14081,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 21 + "lineNumber": 20 }, "initialIsOpen": false }, @@ -18395,7 +18315,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 223 + "lineNumber": 230 } } ], @@ -18403,7 +18323,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 223 + "lineNumber": 230 } }, { @@ -18439,7 +18359,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } }, { @@ -18452,7 +18372,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } } ], @@ -18460,7 +18380,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 233 + "lineNumber": 241 } }, { @@ -18503,7 +18423,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } }, { @@ -18516,7 +18436,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } } ], @@ -18524,7 +18444,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 241 + "lineNumber": 249 } }, { @@ -18548,7 +18468,7 @@ "returnComment": [], "source": { "path": "src/plugins/expressions/common/executor/executor.ts", - "lineNumber": 250 + "lineNumber": 258 } } ], @@ -23230,7 +23150,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 86 + "lineNumber": 71 }, "signature": [ { @@ -23250,7 +23170,7 @@ "description": [], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 87 + "lineNumber": 72 }, "signature": [ { @@ -23265,7 +23185,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 85 + "lineNumber": 70 }, "initialIsOpen": false }, @@ -23362,7 +23282,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 28 + "lineNumber": 27 }, "signature": [ "() => ExecutionContextSearch" @@ -23378,7 +23298,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 33 + "lineNumber": 32 }, "signature": [ "Record" @@ -23394,7 +23314,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 38 + "lineNumber": 37 }, "signature": [ "Record string | undefined" @@ -23466,7 +23386,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 60 + "lineNumber": 59 }, "signature": [ "(() => ", @@ -23480,46 +23400,6 @@ ") | undefined" ] }, - { - "tags": [], - "id": "def-common.ExecutionContext.getSavedObject", - "type": "Function", - "label": "getSavedObject", - "description": [ - "\nAllows to fetch saved objects from ElasticSearch. In browser `getSavedObject`\nfunction is provided automatically by the Expressions plugin. On the server\nthe caller of the expression has to provide this context function. The\nreason is because on the browser we always know the user who tries to\nfetch a saved object, thus saved object client is scoped automatically to\nthat user. However, on the server we can scope that saved object client to\nany user, or even not scope it at all and execute it as an \"internal\" user." - ], - "source": { - "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 71 - }, - "signature": [ - "((type: string, id: string) => Promise<", - { - "pluginId": "core", - "scope": "common", - "docId": "kibCorePluginApi", - "section": "def-common.SavedObject", - "text": "SavedObject" - }, - ">) | undefined" - ] - }, { "tags": [], "id": "def-common.ExecutionContext.isSyncColorsEnabled", @@ -23530,7 +23410,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 79 + "lineNumber": 64 }, "signature": [ "(() => boolean) | undefined" @@ -23539,7 +23419,7 @@ ], "source": { "path": "src/plugins/expressions/common/execution/types.ts", - "lineNumber": 21 + "lineNumber": 20 }, "initialIsOpen": false }, diff --git a/api_docs/fleet.json b/api_docs/fleet.json index ed51f88ee9d5d..60d0dca4d8a10 100644 --- a/api_docs/fleet.json +++ b/api_docs/fleet.json @@ -1789,7 +1789,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, id: string, withPackagePolicies?: boolean) => Promise<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, id: string, withPackagePolicies?: boolean) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -1819,7 +1819,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, options: Readonly<{ page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: \"asc\" | \"desc\" | undefined; kuery?: any; showUpgradeable?: boolean | undefined; } & {}> & { withPackagePolicies?: boolean | undefined; }) => Promise<{ items: ", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, options: Readonly<{ page?: number | undefined; perPage?: number | undefined; sortField?: string | undefined; sortOrder?: \"asc\" | \"desc\" | undefined; kuery?: any; showUpgradeable?: boolean | undefined; } & {}> & { withPackagePolicies?: boolean | undefined; }) => Promise<{ items: ", { "pluginId": "fleet", "scope": "common", @@ -1849,7 +1849,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">) => Promise" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">) => Promise" ] }, { @@ -1871,7 +1871,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, id: string, options?: { standalone: boolean; } | undefined) => Promise<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, id: string, options?: { standalone: boolean; } | undefined) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -2434,7 +2434,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, pkgName: string, datasetPath: string) => Promise" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, pkgName: string, datasetPath: string) => Promise" ], "description": [], "children": [ @@ -2451,7 +2451,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ], "description": [], "source": { @@ -2661,7 +2661,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, pkgName: string) => Promise<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, pkgName: string) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -2686,7 +2686,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ], "description": [], "source": { @@ -3744,7 +3744,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 152 + "lineNumber": 151 } }, { @@ -3755,7 +3755,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 153 + "lineNumber": 152 }, "signature": [ { @@ -3776,7 +3776,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 154 + "lineNumber": 153 }, "signature": [ "string | undefined" @@ -3790,7 +3790,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 155 + "lineNumber": 154 }, "signature": [ "string | undefined" @@ -3804,7 +3804,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 156 + "lineNumber": 155 }, "signature": [ "string[]" @@ -3813,7 +3813,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 151 + "lineNumber": 150 }, "initialIsOpen": false }, @@ -3849,7 +3849,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 49 + "lineNumber": 48 }, "signature": [ { @@ -3869,7 +3869,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 50 + "lineNumber": 49 }, "signature": [ "any" @@ -3883,7 +3883,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 51 + "lineNumber": 50 }, "signature": [ "string | undefined" @@ -3897,7 +3897,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 52 + "lineNumber": 51 } }, { @@ -3908,7 +3908,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 53 + "lineNumber": 52 } }, { @@ -3919,7 +3919,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 54 + "lineNumber": 53 } }, { @@ -3930,7 +3930,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 55 + "lineNumber": 54 }, "signature": [ "any" @@ -3939,7 +3939,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 48 + "lineNumber": 47 }, "initialIsOpen": false }, @@ -3975,13 +3975,13 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 124 + "lineNumber": 123 } } ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 123 + "lineNumber": 122 }, "initialIsOpen": false }, @@ -4000,7 +4000,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 130 + "lineNumber": 129 }, "signature": [ "any" @@ -4009,7 +4009,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 129 + "lineNumber": 128 }, "initialIsOpen": false }, @@ -4174,7 +4174,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 59 + "lineNumber": 58 } }, { @@ -4185,7 +4185,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 60 + "lineNumber": 59 }, "signature": [ { @@ -4205,7 +4205,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 61 + "lineNumber": 60 }, "signature": [ "{ policy: ", @@ -4227,7 +4227,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 64 + "lineNumber": 63 } }, { @@ -4238,7 +4238,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 65 + "lineNumber": 64 } }, { @@ -4249,7 +4249,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 66 + "lineNumber": 65 } }, { @@ -4260,7 +4260,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 67 + "lineNumber": 66 }, "signature": [ "any" @@ -4269,7 +4269,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 58 + "lineNumber": 57 }, "initialIsOpen": false }, @@ -4298,7 +4298,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 160 + "lineNumber": 159 }, "signature": [ "string | undefined" @@ -4312,7 +4312,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 161 + "lineNumber": 160 }, "signature": [ "string[] | undefined" @@ -4321,7 +4321,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 159 + "lineNumber": 158 }, "initialIsOpen": false }, @@ -5111,7 +5111,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 193 + "lineNumber": 200 }, "signature": [ "{ agentId: string; }" @@ -5120,7 +5120,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 192 + "lineNumber": 199 }, "initialIsOpen": false }, @@ -5521,7 +5521,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 173 + "lineNumber": 172 }, "signature": [ "number | undefined" @@ -5537,7 +5537,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 177 + "lineNumber": 176 }, "signature": [ "string | undefined" @@ -5553,7 +5553,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 181 + "lineNumber": 180 }, "signature": [ { @@ -5575,7 +5575,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 185 + "lineNumber": 184 } }, { @@ -5588,7 +5588,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 189 + "lineNumber": 188 } }, { @@ -5601,7 +5601,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 193 + "lineNumber": 192 }, "signature": [ "string | undefined" @@ -5617,7 +5617,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 197 + "lineNumber": 196 }, "signature": [ "string | undefined" @@ -5633,7 +5633,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 201 + "lineNumber": 200 }, "signature": [ "string | null | undefined" @@ -5649,7 +5649,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 205 + "lineNumber": 204 }, "signature": [ "string | null | undefined" @@ -5665,7 +5665,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 209 + "lineNumber": 208 }, "signature": [ "string | undefined" @@ -5679,7 +5679,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 210 + "lineNumber": 209 }, "signature": [ { @@ -5702,7 +5702,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 214 + "lineNumber": 213 }, "signature": [ { @@ -5724,7 +5724,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 218 + "lineNumber": 217 }, "signature": [ { @@ -5746,7 +5746,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 222 + "lineNumber": 221 }, "signature": [ "string | undefined" @@ -5762,7 +5762,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 226 + "lineNumber": 225 }, "signature": [ "number | null | undefined" @@ -5778,7 +5778,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 230 + "lineNumber": 229 }, "signature": [ "number | undefined" @@ -5794,7 +5794,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 234 + "lineNumber": 233 }, "signature": [ "string | undefined" @@ -5810,7 +5810,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 238 + "lineNumber": 237 }, "signature": [ "string | undefined" @@ -5826,7 +5826,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 242 + "lineNumber": 241 }, "signature": [ "\"online\" | \"error\" | \"updating\" | \"degraded\" | undefined" @@ -5842,7 +5842,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 246 + "lineNumber": 245 }, "signature": [ "string | undefined" @@ -5858,7 +5858,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 250 + "lineNumber": 249 }, "signature": [ "string | undefined" @@ -5874,7 +5874,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 254 + "lineNumber": 253 }, "signature": [ "string | undefined" @@ -5890,7 +5890,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 258 + "lineNumber": 257 }, "signature": [ "string[] | undefined" @@ -5906,7 +5906,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 262 + "lineNumber": 261 }, "signature": [ "number | undefined" @@ -5915,7 +5915,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 169 + "lineNumber": 168 }, "initialIsOpen": false }, @@ -5938,7 +5938,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 286 + "lineNumber": 285 }, "signature": [ "string | undefined" @@ -5954,7 +5954,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 290 + "lineNumber": 289 }, "signature": [ "number | undefined" @@ -5970,7 +5970,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 294 + "lineNumber": 293 }, "signature": [ "string | undefined" @@ -5986,7 +5986,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 298 + "lineNumber": 297 }, "signature": [ "string | undefined" @@ -6002,7 +6002,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 302 + "lineNumber": 301 }, "signature": [ "string | undefined" @@ -6018,7 +6018,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 306 + "lineNumber": 305 }, "signature": [ "string | undefined" @@ -6034,7 +6034,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 310 + "lineNumber": 309 }, "signature": [ "string | undefined" @@ -6050,7 +6050,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 314 + "lineNumber": 313 }, "signature": [ "string[] | undefined" @@ -6066,7 +6066,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 318 + "lineNumber": 317 }, "signature": [ "{ [k: string]: unknown; } | undefined" @@ -6080,7 +6080,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 321 + "lineNumber": 320 }, "signature": [ "any" @@ -6089,7 +6089,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 282 + "lineNumber": 281 }, "initialIsOpen": false }, @@ -6112,7 +6112,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 271 + "lineNumber": 270 } }, { @@ -6125,7 +6125,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 275 + "lineNumber": 274 } }, { @@ -6136,7 +6136,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 276 + "lineNumber": 275 }, "signature": [ "any" @@ -6145,7 +6145,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 267 + "lineNumber": 266 }, "initialIsOpen": false }, @@ -6968,7 +6968,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 208 + "lineNumber": 215 }, "signature": [ "{ kuery?: string | undefined; policyId?: string | undefined; }" @@ -6977,7 +6977,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 207 + "lineNumber": 214 }, "initialIsOpen": false }, @@ -6996,7 +6996,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 215 + "lineNumber": 222 }, "signature": [ "{ events: number; total: number; online: number; error: number; offline: number; other: number; updating: number; }" @@ -7005,7 +7005,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 214 + "lineNumber": 221 }, "initialIsOpen": false }, @@ -7436,7 +7436,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 175 + "lineNumber": 182 }, "signature": [ "{ agentId: string; }" @@ -7450,7 +7450,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 178 + "lineNumber": 185 }, "signature": [ "{ page: number; perPage: number; kuery?: string | undefined; }" @@ -7459,7 +7459,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 174 + "lineNumber": 181 }, "initialIsOpen": false }, @@ -7478,7 +7478,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 186 + "lineNumber": 193 }, "signature": [ { @@ -7499,7 +7499,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 187 + "lineNumber": 194 } }, { @@ -7510,7 +7510,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 188 + "lineNumber": 195 } }, { @@ -7521,13 +7521,13 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 189 + "lineNumber": 196 } } ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 185 + "lineNumber": 192 }, "initialIsOpen": false }, @@ -8883,7 +8883,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 43 + "lineNumber": 42 }, "signature": [ { @@ -8903,7 +8903,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 44 + "lineNumber": 43 }, "signature": [ "any" @@ -8917,7 +8917,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 45 + "lineNumber": 44 }, "signature": [ "string | undefined" @@ -8926,7 +8926,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 42 + "lineNumber": 41 }, "initialIsOpen": false }, @@ -8945,7 +8945,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 98 + "lineNumber": 97 }, "signature": [ "\"STATE\" | \"ERROR\" | \"ACTION_RESULT\" | \"ACTION\"" @@ -8959,7 +8959,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 99 + "lineNumber": 98 }, "signature": [ "\"RUNNING\" | \"STARTING\" | \"IN_PROGRESS\" | \"CONFIG\" | \"FAILED\" | \"STOPPING\" | \"STOPPED\" | \"DEGRADED\" | \"UPDATING\" | \"DATA_DUMP\" | \"ACKNOWLEDGED\" | \"UNKNOWN\"" @@ -8973,7 +8973,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 114 + "lineNumber": 113 } }, { @@ -8984,7 +8984,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 115 + "lineNumber": 114 } }, { @@ -8995,7 +8995,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 116 + "lineNumber": 115 }, "signature": [ "any" @@ -9009,7 +9009,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 117 + "lineNumber": 116 } }, { @@ -9020,7 +9020,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 118 + "lineNumber": 117 }, "signature": [ "string | undefined" @@ -9034,7 +9034,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 119 + "lineNumber": 118 }, "signature": [ "string | undefined" @@ -9048,7 +9048,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 120 + "lineNumber": 119 }, "signature": [ "string | undefined" @@ -9057,7 +9057,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 97 + "lineNumber": 96 }, "initialIsOpen": false }, @@ -10708,7 +10708,7 @@ "children": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 148 + "lineNumber": 154 }, "initialIsOpen": false }, @@ -10727,7 +10727,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 161 + "lineNumber": 167 }, "signature": [ "{ policy_id: string; agents: string | string[]; }" @@ -10736,35 +10736,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 160 - }, - "initialIsOpen": false - }, - { - "id": "def-common.PostBulkAgentReassignResponse", - "type": "Interface", - "label": "PostBulkAgentReassignResponse", - "description": [], - "tags": [], - "children": [ - { - "id": "def-common.PostBulkAgentReassignResponse.Unnamed", - "type": "Any", - "label": "Unnamed", - "tags": [], - "description": [], - "source": { - "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 168 - }, - "signature": [ - "any" - ] - } - ], - "source": { - "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 167 + "lineNumber": 166 }, "initialIsOpen": false }, @@ -10837,19 +10809,6 @@ }, "initialIsOpen": false }, - { - "id": "def-common.PostBulkAgentUpgradeResponse", - "type": "Interface", - "label": "PostBulkAgentUpgradeResponse", - "description": [], - "tags": [], - "children": [], - "source": { - "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 145 - }, - "initialIsOpen": false - }, { "id": "def-common.PostEnrollmentAPIKeyRequest", "type": "Interface", @@ -11047,7 +11006,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 151 + "lineNumber": 157 }, "signature": [ "{ agentId: string; }" @@ -11061,7 +11020,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 154 + "lineNumber": 160 }, "signature": [ "{ policy_id: string; }" @@ -11070,7 +11029,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 150 + "lineNumber": 156 }, "initialIsOpen": false }, @@ -11083,7 +11042,7 @@ "children": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 158 + "lineNumber": 164 }, "initialIsOpen": false }, @@ -12138,7 +12097,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 199 + "lineNumber": 206 }, "signature": [ "{ agentId: string; }" @@ -12152,7 +12111,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 202 + "lineNumber": 209 }, "signature": [ "{ user_provided_metadata: Record; }" @@ -12161,7 +12120,7 @@ ], "source": { "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", - "lineNumber": 198 + "lineNumber": 205 }, "initialIsOpen": false }, @@ -12597,7 +12556,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 88 + "lineNumber": 87 }, "signature": [ "CommonAgentActionSOAttributes & { agent_id: string; }" @@ -12615,7 +12574,7 @@ "lineNumber": 34 }, "signature": [ - "\"POLICY_CHANGE\" | \"UNENROLL\" | \"UPGRADE\" | \"SETTINGS\" | \"INTERNAL_POLICY_REASSIGN\"" + "\"POLICY_CHANGE\" | \"UNENROLL\" | \"UPGRADE\" | \"SETTINGS\" | \"POLICY_REASSIGN\"" ], "initialIsOpen": false }, @@ -12642,7 +12601,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 127 + "lineNumber": 126 }, "signature": [ "NewAgentEvent" @@ -12657,7 +12616,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 91 + "lineNumber": 90 }, "signature": [ "CommonAgentActionSOAttributes & { policy_id: string; policy_revision: number; }" @@ -12672,7 +12631,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 72 + "lineNumber": 71 }, "signature": [ "Pick & { type: 'CONFIG_CHANGE'; data: { config: FullAgentPolicy;}; }" @@ -12920,7 +12879,7 @@ "description": [], "source": { "path": "x-pack/plugins/fleet/common/types/models/agent.ts", - "lineNumber": 95 + "lineNumber": 94 }, "signature": [ { @@ -13822,6 +13781,36 @@ ], "initialIsOpen": false }, + { + "id": "def-common.PostBulkAgentReassignResponse", + "type": "Type", + "label": "PostBulkAgentReassignResponse", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", + "lineNumber": 173 + }, + "signature": [ + "{ [x: string]: { success: boolean; error?: string | undefined; }; }" + ], + "initialIsOpen": false + }, + { + "id": "def-common.PostBulkAgentUpgradeResponse", + "type": "Type", + "label": "PostBulkAgentUpgradeResponse", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/fleet/common/types/rest_spec/agent.ts", + "lineNumber": 145 + }, + "signature": [ + "{ [x: string]: { success: boolean; error?: string | undefined; }; }" + ], + "initialIsOpen": false + }, { "id": "def-common.RegistryPackage", "type": "Type", diff --git a/api_docs/global_search.json b/api_docs/global_search.json index 985abf3417935..0cb40be77a7ae 100644 --- a/api_docs/global_search.json +++ b/api_docs/global_search.json @@ -704,7 +704,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">; typeRegistry: Pick<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">; typeRegistry: Pick<", { "pluginId": "core", "scope": "server", diff --git a/api_docs/index_management.json b/api_docs/index_management.json index 8c6ba711642ff..6c66a94bf59f5 100644 --- a/api_docs/index_management.json +++ b/api_docs/index_management.json @@ -1121,13 +1121,7 @@ "lineNumber": 58 }, "signature": [ - { - "pluginId": "indexManagement", - "scope": "common", - "docId": "kibIndexManagementPluginApi", - "section": "def-common.Health", - "text": "Health" - } + "ClusterStatus" ] }, { diff --git a/api_docs/infra.json b/api_docs/infra.json index a10be8ce8bf04..9d64df881fc52 100644 --- a/api_docs/infra.json +++ b/api_docs/infra.json @@ -198,7 +198,7 @@ "description": [], "source": { "path": "x-pack/plugins/infra/server/plugin.ts", - "lineNumber": 64 + "lineNumber": 65 }, "signature": [ "{ readonly sources?: Readonly<{ default?: Readonly<{ fields?: Readonly<{ host?: string | undefined; message?: string[] | undefined; timestamp?: string | undefined; tiebreaker?: string | undefined; container?: string | undefined; pod?: string | undefined; } & {}> | undefined; logAlias?: string | undefined; metricAlias?: string | undefined; } & {}> | undefined; } & {}> | undefined; readonly enabled: boolean; readonly query: Readonly<{} & { partitionSize: number; partitionFactor: number; }>; }" @@ -222,7 +222,7 @@ "description": [], "source": { "path": "x-pack/plugins/infra/server/plugin.ts", - "lineNumber": 75 + "lineNumber": 76 }, "signature": [ "(sourceId: string, sourceProperties: ", @@ -239,7 +239,7 @@ ], "source": { "path": "x-pack/plugins/infra/server/plugin.ts", - "lineNumber": 74 + "lineNumber": 75 }, "lifecycle": "setup", "initialIsOpen": true diff --git a/api_docs/lens.json b/api_docs/lens.json index e586016c22fc3..b5321bf24ba6b 100644 --- a/api_docs/lens.json +++ b/api_docs/lens.json @@ -82,7 +82,7 @@ "lineNumber": 43 }, "signature": [ - "\"cardinality\"" + "\"unique_count\"" ] } ], @@ -238,7 +238,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx", - "lineNumber": 73 + "lineNumber": 74 }, "signature": [ "\"filters\"" @@ -252,7 +252,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx", - "lineNumber": 74 + "lineNumber": 75 }, "signature": [ "{ filters: ", @@ -269,7 +269,7 @@ ], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/filters/filters.tsx", - "lineNumber": 72 + "lineNumber": 73 }, "initialIsOpen": false }, @@ -291,7 +291,7 @@ "lineNumber": 46 }, "signature": [ - "\"range\" | \"filters\" | \"count\" | \"max\" | \"min\" | \"date_histogram\" | \"sum\" | \"terms\" | \"avg\" | \"median\" | \"cumulative_sum\" | \"derivative\" | \"moving_average\" | \"counter_rate\" | \"cardinality\" | \"percentile\" | \"last_value\" | undefined" + "\"range\" | \"filters\" | \"count\" | \"max\" | \"min\" | \"date_histogram\" | \"sum\" | \"average\" | \"terms\" | \"median\" | \"cumulative_sum\" | \"moving_average\" | \"counter_rate\" | \"differences\" | \"unique_count\" | \"percentile\" | \"last_value\" | undefined" ] }, { @@ -538,6 +538,30 @@ "signature": [ "() => boolean" ] + }, + { + "tags": [], + "id": "def-public.LensPublicStart.getXyVisTypes", + "type": "Function", + "label": "getXyVisTypes", + "description": [ + "\nMethod which returns xy VisualizationTypes array keeping this async as to not impact page load bundle" + ], + "source": { + "path": "x-pack/plugins/lens/public/plugin.ts", + "lineNumber": 108 + }, + "signature": [ + "() => Promise<", + { + "pluginId": "lens", + "scope": "public", + "docId": "kibLensPluginApi", + "section": "def-public.VisualizationType", + "text": "VisualizationType" + }, + "[]>" + ] } ], "source": { @@ -929,7 +953,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx", - "lineNumber": 59 + "lineNumber": 57 }, "signature": [ "\"terms\"" @@ -943,7 +967,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx", - "lineNumber": 60 + "lineNumber": 58 }, "signature": [ "{ size: number; orderBy: { type: \"alphabetical\"; } | { type: \"column\"; columnId: string; }; orderDirection: \"asc\" | \"desc\"; otherBucket?: boolean | undefined; missingBucket?: boolean | undefined; format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; }" @@ -952,7 +976,7 @@ ], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx", - "lineNumber": 58 + "lineNumber": 56 }, "initialIsOpen": false }, @@ -1114,7 +1138,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 420 + "lineNumber": 423 }, "signature": [ { @@ -1134,7 +1158,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 421 + "lineNumber": 424 }, "signature": [ { @@ -1154,7 +1178,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 422 + "lineNumber": 425 }, "signature": [ "\"hide\" | \"inside\" | \"outside\" | undefined" @@ -1168,7 +1192,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 423 + "lineNumber": 426 }, "signature": [ "\"None\" | \"Zero\" | \"Linear\" | \"Carry\" | \"Lookahead\" | undefined" @@ -1182,7 +1206,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 424 + "lineNumber": 427 }, "signature": [ { @@ -1203,7 +1227,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 425 + "lineNumber": 428 }, "signature": [ "string | undefined" @@ -1217,7 +1241,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 426 + "lineNumber": 429 }, "signature": [ "string | undefined" @@ -1231,7 +1255,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 427 + "lineNumber": 430 }, "signature": [ "string | undefined" @@ -1245,7 +1269,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 428 + "lineNumber": 431 }, "signature": [ { @@ -1266,7 +1290,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 429 + "lineNumber": 432 }, "signature": [ { @@ -1287,7 +1311,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 430 + "lineNumber": 433 }, "signature": [ { @@ -1299,11 +1323,25 @@ }, " | undefined" ] + }, + { + "tags": [], + "id": "def-public.XYState.curveType", + "type": "CompoundType", + "label": "curveType", + "description": [], + "source": { + "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", + "lineNumber": 434 + }, + "signature": [ + "\"LINEAR\" | \"CURVE_MONOTONE_X\" | undefined" + ] } ], "source": { "path": "x-pack/plugins/lens/public/xy_visualization/types.ts", - "lineNumber": 419 + "lineNumber": 422 }, "initialIsOpen": false } @@ -1318,10 +1356,10 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx", - "lineNumber": 127 + "lineNumber": 126 }, "signature": [ - "BaseIndexPatternColumn & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & FieldBasedIndexPatternColumn & { operationType: \"avg\"; }" + "BaseIndexPatternColumn & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & FieldBasedIndexPatternColumn & { operationType: \"average\"; }" ], "initialIsOpen": false }, @@ -1380,12 +1418,12 @@ "tags": [], "description": [], "source": { - "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/derivative.tsx", - "lineNumber": 35 + "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/differences.tsx", + "lineNumber": 37 }, "signature": [ "BaseIndexPatternColumn", - " & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & ReferenceBasedIndexPatternColumn & { operationType: 'derivative'; }" + " & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & ReferenceBasedIndexPatternColumn & { operationType: typeof OPERATION_NAME; }" ], "initialIsOpen": false }, @@ -1456,7 +1494,7 @@ "section": "def-public.DateHistogramIndexPatternColumn", "text": "DateHistogramIndexPatternColumn" }, - " | MetricColumn<\"min\"> | MetricColumn<\"max\"> | MetricColumn<\"avg\"> | ", + " | MetricColumn<\"min\"> | MetricColumn<\"max\"> | MetricColumn<\"average\"> | ", { "pluginId": "lens", "scope": "public", @@ -1475,7 +1513,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx", - "lineNumber": 129 + "lineNumber": 128 }, "signature": [ "BaseIndexPatternColumn & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & FieldBasedIndexPatternColumn & { operationType: \"max\"; }" @@ -1490,7 +1528,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx", - "lineNumber": 130 + "lineNumber": 129 }, "signature": [ "BaseIndexPatternColumn & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & FieldBasedIndexPatternColumn & { operationType: \"median\"; }" @@ -1505,7 +1543,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx", - "lineNumber": 128 + "lineNumber": 127 }, "signature": [ "BaseIndexPatternColumn & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & FieldBasedIndexPatternColumn & { operationType: \"min\"; }" @@ -1541,7 +1579,7 @@ "lineNumber": 406 }, "signature": [ - "\"range\" | \"filters\" | \"count\" | \"max\" | \"min\" | \"date_histogram\" | \"sum\" | \"terms\" | \"avg\" | \"median\" | \"cumulative_sum\" | \"derivative\" | \"moving_average\" | \"counter_rate\" | \"cardinality\" | \"percentile\" | \"last_value\"" + "\"range\" | \"filters\" | \"count\" | \"max\" | \"min\" | \"date_histogram\" | \"sum\" | \"average\" | \"terms\" | \"median\" | \"cumulative_sum\" | \"moving_average\" | \"counter_rate\" | \"differences\" | \"unique_count\" | \"percentile\" | \"last_value\"" ], "initialIsOpen": false }, @@ -1598,7 +1636,7 @@ "description": [], "source": { "path": "x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx", - "lineNumber": 126 + "lineNumber": 125 }, "signature": [ "BaseIndexPatternColumn & { params?: { format?: { id: string; params?: { decimals: number; } | undefined; } | undefined; } | undefined; } & FieldBasedIndexPatternColumn & { operationType: \"sum\"; }" diff --git a/api_docs/lists.json b/api_docs/lists.json index fe06ebe62ce23..fc935809cd7d8 100644 --- a/api_docs/lists.json +++ b/api_docs/lists.json @@ -3698,6 +3698,188 @@ "lineNumber": 48 }, "initialIsOpen": false + }, + { + "id": "def-server.UpdateExceptionListItemOptions", + "type": "Interface", + "label": "UpdateExceptionListItemOptions", + "description": [], + "tags": [], + "children": [ + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions._version", + "type": "string", + "label": "_version", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 144 + }, + "signature": [ + "string | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.comments", + "type": "Array", + "label": "comments", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 145 + }, + "signature": [ + "({ comment: string; } & { id?: string | undefined; })[]" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.entries", + "type": "Array", + "label": "entries", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 146 + }, + "signature": [ + "({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; list: { id: string; type: \"boolean\" | \"date\" | \"text\" | \"keyword\" | \"ip\" | \"long\" | \"double\" | \"date_nanos\" | \"geo_point\" | \"geo_shape\" | \"short\" | \"binary\" | \"date_range\" | \"ip_range\" | \"shape\" | \"integer\" | \"byte\" | \"float\" | \"double_range\" | \"float_range\" | \"half_float\" | \"integer_range\" | \"long_range\"; }; operator: \"excluded\" | \"included\"; type: \"list\"; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; } | { entries: ({ field: string; operator: \"excluded\" | \"included\"; type: \"match\"; value: string; } | { field: string; operator: \"excluded\" | \"included\"; type: \"match_any\"; value: string[]; } | { field: string; operator: \"excluded\" | \"included\"; type: \"exists\"; })[]; field: string; type: \"nested\"; })[]" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.id", + "type": "string", + "label": "id", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 147 + }, + "signature": [ + "string | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.itemId", + "type": "string", + "label": "itemId", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 148 + }, + "signature": [ + "string | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.namespaceType", + "type": "CompoundType", + "label": "namespaceType", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 149 + }, + "signature": [ + "\"single\" | \"agnostic\"" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.name", + "type": "string", + "label": "name", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 150 + }, + "signature": [ + "string | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.osTypes", + "type": "Array", + "label": "osTypes", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 151 + }, + "signature": [ + "(\"windows\" | \"linux\" | \"macos\")[]" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.description", + "type": "string", + "label": "description", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 152 + }, + "signature": [ + "string | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.meta", + "type": "Uncategorized", + "label": "meta", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 153 + }, + "signature": [ + "object | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.tags", + "type": "Array", + "label": "tags", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 154 + }, + "signature": [ + "string[] | undefined" + ] + }, + { + "tags": [], + "id": "def-server.UpdateExceptionListItemOptions.type", + "type": "string", + "label": "type", + "description": [], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 155 + }, + "signature": [ + "\"simple\" | undefined" + ] + } + ], + "source": { + "path": "x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts", + "lineNumber": 143 + }, + "initialIsOpen": false } ], "enums": [], diff --git a/api_docs/ml.json b/api_docs/ml.json index fa9bd613195a6..067ae4c0ea212 100644 --- a/api_docs/ml.json +++ b/api_docs/ml.json @@ -1626,13 +1626,7 @@ "isRequired": false, "signature": [ "Record | undefined" ], "description": [], @@ -1700,210 +1694,6 @@ } ], "interfaces": [ - { - "id": "def-server.AnalysisConfig", - "type": "Interface", - "label": "AnalysisConfig", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.AnalysisConfig.bucket_span", - "type": "string", - "label": "bucket_span", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 48 - } - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.categorization_field_name", - "type": "string", - "label": "categorization_field_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 49 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.categorization_filters", - "type": "Array", - "label": "categorization_filters", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 50 - }, - "signature": [ - "string[] | undefined" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.categorization_analyzer", - "type": "CompoundType", - "label": "categorization_analyzer", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 51 - }, - "signature": [ - "string | object | undefined" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.detectors", - "type": "Array", - "label": "detectors", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 52 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.Detector", - "text": "Detector" - }, - "[]" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.influencers", - "type": "Array", - "label": "influencers", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 53 - }, - "signature": [ - "string[]" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.latency", - "type": "number", - "label": "latency", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 54 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.multivariate_by_fields", - "type": "CompoundType", - "label": "multivariate_by_fields", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 55 - }, - "signature": [ - "boolean | undefined" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.summary_count_field_name", - "type": "string", - "label": "summary_count_field_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 56 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisConfig.per_partition_categorization", - "type": "Object", - "label": "per_partition_categorization", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 57 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.PerPartitionCategorization", - "text": "PerPartitionCategorization" - }, - " | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 47 - }, - "initialIsOpen": false - }, - { - "id": "def-server.AnalysisLimits", - "type": "Interface", - "label": "AnalysisLimits", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.AnalysisLimits.categorization_examples_limit", - "type": "number", - "label": "categorization_examples_limit", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 73 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.AnalysisLimits.model_memory_limit", - "type": "string", - "label": "model_memory_limit", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 74 - } - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 72 - }, - "initialIsOpen": false - }, { "id": "def-server.AnomaliesTableRecord", "type": "Interface", @@ -2692,48 +2482,6 @@ }, "initialIsOpen": false }, - { - "id": "def-server.ChunkingConfig", - "type": "Interface", - "label": "ChunkingConfig", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.ChunkingConfig.mode", - "type": "CompoundType", - "label": "mode", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 32 - }, - "signature": [ - "\"auto\" | \"off\" | \"manual\"" - ] - }, - { - "tags": [], - "id": "def-server.ChunkingConfig.time_span", - "type": "string", - "label": "time_span", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 33 - }, - "signature": [ - "string | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 31 - }, - "initialIsOpen": false - }, { "id": "def-server.CombinedJob", "type": "Interface", @@ -2771,13 +2519,7 @@ "lineNumber": 20 }, "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.Datafeed", - "text": "Datafeed" - } + "Datafeed" ] } ], @@ -2841,128 +2583,72 @@ "initialIsOpen": false }, { - "id": "def-server.CustomRule", + "id": "def-server.CustomSettings", "type": "Interface", - "label": "CustomRule", + "label": "CustomSettings", "description": [], "tags": [], "children": [ { "tags": [], - "id": "def-server.CustomRule.actions", + "id": "def-server.CustomSettings.custom_urls", "type": "Array", - "label": "actions", + "label": "custom_urls", "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 91 + "lineNumber": 16 }, "signature": [ - "string[]" + { + "pluginId": "ml", + "scope": "common", + "docId": "kibMlPluginApi", + "section": "def-common.UrlConfig", + "text": "UrlConfig" + }, + "[] | undefined" ] }, { "tags": [], - "id": "def-server.CustomRule.scope", - "type": "Uncategorized", - "label": "scope", + "id": "def-server.CustomSettings.created_by", + "type": "CompoundType", + "label": "created_by", "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 92 + "lineNumber": 17 }, "signature": [ - "object | undefined" + { + "pluginId": "ml", + "scope": "common", + "docId": "kibMlPluginApi", + "section": "def-common.CREATED_BY_LABEL", + "text": "CREATED_BY_LABEL" + }, + " | undefined" ] }, { "tags": [], - "id": "def-server.CustomRule.conditions", - "type": "Array", - "label": "conditions", + "id": "def-server.CustomSettings.job_tags", + "type": "Object", + "label": "job_tags", "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 93 + "lineNumber": 18 }, "signature": [ - "any[]" + "{ [tag: string]: string; } | undefined" ] } ], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 90 - }, - "initialIsOpen": false - }, - { - "id": "def-server.CustomSettings", - "type": "Interface", - "label": "CustomSettings", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.CustomSettings.custom_urls", - "type": "Array", - "label": "custom_urls", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 15 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.UrlConfig", - "text": "UrlConfig" - }, - "[] | undefined" - ] - }, - { - "tags": [], - "id": "def-server.CustomSettings.created_by", - "type": "CompoundType", - "label": "created_by", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 16 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.CREATED_BY_LABEL", - "text": "CREATED_BY_LABEL" - }, - " | undefined" - ] - }, - { - "tags": [], - "id": "def-server.CustomSettings.job_tags", - "type": "Object", - "label": "job_tags", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 17 - }, - "signature": [ - "{ [tag: string]: string; } | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 14 + "lineNumber": 15 }, "initialIsOpen": false }, @@ -3182,1065 +2868,223 @@ "initialIsOpen": false }, { - "id": "def-server.DataDescription", - "type": "Interface", - "label": "DataDescription", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.DataDescription.format", - "type": "string", - "label": "format", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 78 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.DataDescription.time_field", - "type": "string", - "label": "time_field", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 79 - } - }, - { - "tags": [], - "id": "def-server.DataDescription.time_format", - "type": "string", - "label": "time_format", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 80 - }, - "signature": [ - "string | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 77 - }, - "initialIsOpen": false - }, - { - "id": "def-server.Datafeed", + "id": "def-server.DatafeedStats", "type": "Interface", - "label": "Datafeed", + "label": "DatafeedStats", "description": [], "tags": [], "children": [ { "tags": [], - "id": "def-server.Datafeed.datafeed_id", + "id": "def-server.DatafeedStats.datafeed_id", "type": "string", "label": "datafeed_id", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 14 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", + "lineNumber": 12 } }, { "tags": [], - "id": "def-server.Datafeed.aggregations", - "type": "Object", - "label": "aggregations", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 15 - }, - "signature": [ - "Record | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Datafeed.aggs", - "type": "Object", - "label": "aggs", + "id": "def-server.DatafeedStats.state", + "type": "Enum", + "label": "state", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 16 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", + "lineNumber": 13 }, "signature": [ - "Record | undefined" + { + "pluginId": "ml", + "scope": "common", + "docId": "kibMlPluginApi", + "section": "def-common.DATAFEED_STATE", + "text": "DATAFEED_STATE" + } ] }, { "tags": [], - "id": "def-server.Datafeed.chunking_config", + "id": "def-server.DatafeedStats.node", "type": "Object", - "label": "chunking_config", + "label": "node", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 17 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", + "lineNumber": 14 }, "signature": [ { "pluginId": "ml", "scope": "common", "docId": "kibMlPluginApi", - "section": "def-common.ChunkingConfig", - "text": "ChunkingConfig" - }, - " | undefined" + "section": "def-common.Node", + "text": "Node" + } ] }, { "tags": [], - "id": "def-server.Datafeed.frequency", + "id": "def-server.DatafeedStats.assignment_explanation", "type": "string", - "label": "frequency", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 18 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Datafeed.indices", - "type": "Array", - "label": "indices", + "label": "assignment_explanation", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 19 - }, - "signature": [ - "string[]" - ] + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", + "lineNumber": 15 + } }, { "tags": [], - "id": "def-server.Datafeed.indexes", - "type": "Array", - "label": "indexes", + "id": "def-server.DatafeedStats.timing_stats", + "type": "Object", + "label": "timing_stats", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 20 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", + "lineNumber": 16 }, "signature": [ - "string[] | undefined" + "TimingStats" ] - }, + } + ], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", + "lineNumber": 11 + }, + "initialIsOpen": false + }, + { + "id": "def-server.ForecastsStats", + "type": "Interface", + "label": "ForecastsStats", + "description": [], + "tags": [], + "children": [ { "tags": [], - "id": "def-server.Datafeed.job_id", - "type": "string", - "label": "job_id", + "id": "def-server.ForecastsStats.total", + "type": "number", + "label": "total", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 21 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", + "lineNumber": 66 } }, { "tags": [], - "id": "def-server.Datafeed.query", - "type": "Uncategorized", - "label": "query", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 22 - }, - "signature": [ - "object" - ] - }, - { - "tags": [], - "id": "def-server.Datafeed.query_delay", - "type": "string", - "label": "query_delay", + "id": "def-server.ForecastsStats.forecasted_jobs", + "type": "number", + "label": "forecasted_jobs", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 23 - }, - "signature": [ - "string | undefined" - ] + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", + "lineNumber": 67 + } }, { "tags": [], - "id": "def-server.Datafeed.script_fields", - "type": "Object", - "label": "script_fields", + "id": "def-server.ForecastsStats.memory_bytes", + "type": "Any", + "label": "memory_bytes", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 24 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", + "lineNumber": 68 }, "signature": [ - "Record | undefined" + "any" ] }, { "tags": [], - "id": "def-server.Datafeed.runtime_mappings", - "type": "Object", - "label": "runtime_mappings", + "id": "def-server.ForecastsStats.records", + "type": "Any", + "label": "records", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 25 - }, - "signature": [ - "Record | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Datafeed.scroll_size", - "type": "number", - "label": "scroll_size", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 26 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Datafeed.delayed_data_check_config", - "type": "Uncategorized", - "label": "delayed_data_check_config", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 27 - }, - "signature": [ - "object | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Datafeed.indices_options", - "type": "Object", - "label": "indices_options", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 28 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.IndicesOptions", - "text": "IndicesOptions" - }, - " | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 13 - }, - "initialIsOpen": false - }, - { - "id": "def-server.DatafeedStats", - "type": "Interface", - "label": "DatafeedStats", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.DatafeedStats.datafeed_id", - "type": "string", - "label": "datafeed_id", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", - "lineNumber": 12 - } - }, - { - "tags": [], - "id": "def-server.DatafeedStats.state", - "type": "Enum", - "label": "state", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", - "lineNumber": 13 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.DATAFEED_STATE", - "text": "DATAFEED_STATE" - } - ] - }, - { - "tags": [], - "id": "def-server.DatafeedStats.node", - "type": "Object", - "label": "node", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", - "lineNumber": 14 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.Node", - "text": "Node" - } - ] - }, - { - "tags": [], - "id": "def-server.DatafeedStats.assignment_explanation", - "type": "string", - "label": "assignment_explanation", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", - "lineNumber": 15 - } - }, - { - "tags": [], - "id": "def-server.DatafeedStats.timing_stats", - "type": "Object", - "label": "timing_stats", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", - "lineNumber": 16 - }, - "signature": [ - "TimingStats" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed_stats.ts", - "lineNumber": 11 - }, - "initialIsOpen": false - }, - { - "id": "def-server.Detector", - "type": "Interface", - "label": "Detector", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.Detector.by_field_name", - "type": "string", - "label": "by_field_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 61 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.detector_description", - "type": "string", - "label": "detector_description", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 62 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.detector_index", - "type": "number", - "label": "detector_index", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 63 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.exclude_frequent", - "type": "string", - "label": "exclude_frequent", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 64 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.field_name", - "type": "string", - "label": "field_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 65 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.function", - "type": "string", - "label": "function", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 66 - } - }, - { - "tags": [], - "id": "def-server.Detector.over_field_name", - "type": "string", - "label": "over_field_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 67 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.partition_field_name", - "type": "string", - "label": "partition_field_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 68 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.use_null", - "type": "CompoundType", - "label": "use_null", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 69 - }, - "signature": [ - "boolean | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Detector.custom_rules", - "type": "Array", - "label": "custom_rules", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 70 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.CustomRule", - "text": "CustomRule" - }, - "[] | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 60 - }, - "initialIsOpen": false - }, - { - "id": "def-server.ForecastsStats", - "type": "Interface", - "label": "ForecastsStats", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.ForecastsStats.total", - "type": "number", - "label": "total", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", - "lineNumber": 66 - } - }, - { - "tags": [], - "id": "def-server.ForecastsStats.forecasted_jobs", - "type": "number", - "label": "forecasted_jobs", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", - "lineNumber": 67 - } - }, - { - "tags": [], - "id": "def-server.ForecastsStats.memory_bytes", - "type": "Any", - "label": "memory_bytes", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", - "lineNumber": 68 - }, - "signature": [ - "any" - ] - }, - { - "tags": [], - "id": "def-server.ForecastsStats.records", - "type": "Any", - "label": "records", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", - "lineNumber": 69 - }, - "signature": [ - "any" - ] - }, - { - "tags": [], - "id": "def-server.ForecastsStats.processing_time_ms", - "type": "Any", - "label": "processing_time_ms", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", - "lineNumber": 70 - }, - "signature": [ - "any" - ] - }, - { - "tags": [], - "id": "def-server.ForecastsStats.status", - "type": "Any", - "label": "status", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", - "lineNumber": 71 - }, - "signature": [ - "any" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", - "lineNumber": 65 - }, - "initialIsOpen": false - }, - { - "id": "def-server.IndicesOptions", - "type": "Interface", - "label": "IndicesOptions", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.IndicesOptions.expand_wildcards", - "type": "CompoundType", - "label": "expand_wildcards", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 49 - }, - "signature": [ - "\"all\" | \"none\" | \"hidden\" | \"open\" | \"closed\" | undefined" - ] - }, - { - "tags": [], - "id": "def-server.IndicesOptions.ignore_unavailable", - "type": "CompoundType", - "label": "ignore_unavailable", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 50 - }, - "signature": [ - "boolean | undefined" - ] - }, - { - "tags": [], - "id": "def-server.IndicesOptions.allow_no_indices", - "type": "CompoundType", - "label": "allow_no_indices", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 51 - }, - "signature": [ - "boolean | undefined" - ] - }, - { - "tags": [], - "id": "def-server.IndicesOptions.ignore_throttled", - "type": "CompoundType", - "label": "ignore_throttled", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 52 - }, - "signature": [ - "boolean | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 48 - }, - "initialIsOpen": false - }, - { - "id": "def-server.Influencer", - "type": "Interface", - "label": "Influencer", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.Influencer.influencer_field_name", - "type": "string", - "label": "influencer_field_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomalies.ts", - "lineNumber": 11 - } - }, - { - "tags": [], - "id": "def-server.Influencer.influencer_field_values", - "type": "Array", - "label": "influencer_field_values", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomalies.ts", - "lineNumber": 12 - }, - "signature": [ - "string[]" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomalies.ts", - "lineNumber": 10 - }, - "initialIsOpen": false - }, - { - "id": "def-server.Job", - "type": "Interface", - "label": "Job", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.Job.job_id", - "type": "string", - "label": "job_id", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 23 - } - }, - { - "tags": [], - "id": "def-server.Job.analysis_config", - "type": "Object", - "label": "analysis_config", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 24 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.AnalysisConfig", - "text": "AnalysisConfig" - } - ] - }, - { - "tags": [], - "id": "def-server.Job.analysis_limits", - "type": "Object", - "label": "analysis_limits", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 25 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.AnalysisLimits", - "text": "AnalysisLimits" - }, - " | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.background_persist_interval", - "type": "string", - "label": "background_persist_interval", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 26 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.custom_settings", - "type": "Object", - "label": "custom_settings", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 27 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.CustomSettings", - "text": "CustomSettings" - }, - " | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.data_description", - "type": "Object", - "label": "data_description", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 28 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.DataDescription", - "text": "DataDescription" - } - ] - }, - { - "tags": [], - "id": "def-server.Job.description", - "type": "string", - "label": "description", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 29 - } - }, - { - "tags": [], - "id": "def-server.Job.groups", - "type": "Array", - "label": "groups", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 30 - }, - "signature": [ - "string[]" - ] - }, - { - "tags": [], - "id": "def-server.Job.model_plot_config", - "type": "Object", - "label": "model_plot_config", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 31 - }, - "signature": [ - { - "pluginId": "ml", - "scope": "common", - "docId": "kibMlPluginApi", - "section": "def-common.ModelPlotConfig", - "text": "ModelPlotConfig" - }, - " | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.model_snapshot_retention_days", - "type": "number", - "label": "model_snapshot_retention_days", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 32 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.daily_model_snapshot_retention_after_days", - "type": "number", - "label": "daily_model_snapshot_retention_after_days", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 33 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.renormalization_window_days", - "type": "number", - "label": "renormalization_window_days", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 34 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.results_index_name", - "type": "string", - "label": "results_index_name", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 35 - }, - "signature": [ - "string | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.results_retention_days", - "type": "number", - "label": "results_retention_days", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 36 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.create_time", - "type": "number", - "label": "create_time", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 39 - }, - "signature": [ - "number | undefined" - ] - }, - { - "tags": [], - "id": "def-server.Job.finished_time", - "type": "number", - "label": "finished_time", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 40 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", + "lineNumber": 69 }, "signature": [ - "number | undefined" + "any" ] }, { "tags": [], - "id": "def-server.Job.job_type", - "type": "string", - "label": "job_type", + "id": "def-server.ForecastsStats.processing_time_ms", + "type": "Any", + "label": "processing_time_ms", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 41 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", + "lineNumber": 70 }, "signature": [ - "\"anomaly_detector\" | undefined" + "any" ] }, { "tags": [], - "id": "def-server.Job.job_version", - "type": "string", - "label": "job_version", + "id": "def-server.ForecastsStats.status", + "type": "Any", + "label": "status", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 42 + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", + "lineNumber": 71 }, "signature": [ - "string | undefined" + "any" ] - }, + } + ], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job_stats.ts", + "lineNumber": 65 + }, + "initialIsOpen": false + }, + { + "id": "def-server.Influencer", + "type": "Interface", + "label": "Influencer", + "description": [], + "tags": [], + "children": [ { "tags": [], - "id": "def-server.Job.model_snapshot_id", + "id": "def-server.Influencer.influencer_field_name", "type": "string", - "label": "model_snapshot_id", + "label": "influencer_field_name", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 43 - }, - "signature": [ - "string | undefined" - ] + "path": "x-pack/plugins/ml/common/types/anomalies.ts", + "lineNumber": 11 + } }, { "tags": [], - "id": "def-server.Job.deleting", - "type": "CompoundType", - "label": "deleting", + "id": "def-server.Influencer.influencer_field_values", + "type": "Array", + "label": "influencer_field_values", "description": [], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 44 + "path": "x-pack/plugins/ml/common/types/anomalies.ts", + "lineNumber": 12 }, "signature": [ - "boolean | undefined" + "string[]" ] } ], "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 22 + "path": "x-pack/plugins/ml/common/types/anomalies.ts", + "lineNumber": 10 }, "initialIsOpen": false }, @@ -4790,62 +3634,6 @@ }, "initialIsOpen": false }, - { - "id": "def-server.ModelPlotConfig", - "type": "Interface", - "label": "ModelPlotConfig", - "description": [], - "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.ModelPlotConfig.enabled", - "type": "CompoundType", - "label": "enabled", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 84 - }, - "signature": [ - "boolean | undefined" - ] - }, - { - "tags": [], - "id": "def-server.ModelPlotConfig.annotations_enabled", - "type": "CompoundType", - "label": "annotations_enabled", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 85 - }, - "signature": [ - "boolean | undefined" - ] - }, - { - "tags": [], - "id": "def-server.ModelPlotConfig.terms", - "type": "string", - "label": "terms", - "description": [], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 86 - }, - "signature": [ - "string | undefined" - ] - } - ], - "source": { - "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 83 - }, - "initialIsOpen": false - }, { "id": "def-server.ModelSizeStats", "type": "Interface", @@ -5298,7 +4086,7 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 97 + "lineNumber": 106 }, "signature": [ "boolean | undefined" @@ -5312,7 +4100,7 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 98 + "lineNumber": 107 }, "signature": [ "boolean | undefined" @@ -5321,7 +4109,7 @@ ], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 96 + "lineNumber": 105 }, "initialIsOpen": false } @@ -5336,13 +4124,43 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 36 + "lineNumber": 44 }, "signature": [ "{ [x: string]: { date_histogram: { field: string; fixed_interval: string;}; aggregations?: { [key: string]: any; } | undefined; aggs?: { [key: string]: any; } | undefined; }; }" ], "initialIsOpen": false }, + { + "id": "def-server.AnalysisConfig", + "type": "Type", + "label": "AnalysisConfig", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", + "lineNumber": 49 + }, + "signature": [ + "estypes.AnalysisConfig" + ], + "initialIsOpen": false + }, + { + "id": "def-server.AnalysisLimits", + "type": "Type", + "label": "AnalysisLimits", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", + "lineNumber": 77 + }, + "signature": [ + "estypes.AnalysisLimits" + ], + "initialIsOpen": false + }, { "id": "def-server.AnomalyResultType", "type": "Type", @@ -5366,13 +4184,73 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 12 + "lineNumber": 13 }, "signature": [ "string" ], "initialIsOpen": false }, + { + "id": "def-server.ChunkingConfig", + "type": "Type", + "label": "ChunkingConfig", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", + "lineNumber": 37 + }, + "signature": [ + "estypes.ChunkingConfig" + ], + "initialIsOpen": false + }, + { + "id": "def-server.CustomRule", + "type": "Type", + "label": "CustomRule", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", + "lineNumber": 97 + }, + "signature": [ + "estypes.DetectionRule" + ], + "initialIsOpen": false + }, + { + "id": "def-server.DataDescription", + "type": "Type", + "label": "DataDescription", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", + "lineNumber": 83 + }, + "signature": [ + "estypes.DataDescription" + ], + "initialIsOpen": false + }, + { + "id": "def-server.Datafeed", + "type": "Type", + "label": "Datafeed", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", + "lineNumber": 14 + }, + "signature": [ + "estypes.Datafeed" + ], + "initialIsOpen": false + }, { "id": "def-server.DatafeedId", "type": "Type", @@ -5381,7 +4259,7 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", - "lineNumber": 11 + "lineNumber": 12 }, "signature": [ "string" @@ -5399,7 +4277,23 @@ "lineNumber": 14 }, "signature": [ - "Datafeed & DatafeedStats" + "Datafeed", + " & DatafeedStats" + ], + "initialIsOpen": false + }, + { + "id": "def-server.Detector", + "type": "Type", + "label": "Detector", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", + "lineNumber": 63 + }, + "signature": [ + "estypes.Detector" ], "initialIsOpen": false }, @@ -5418,6 +4312,36 @@ ], "initialIsOpen": false }, + { + "id": "def-server.IndicesOptions", + "type": "Type", + "label": "IndicesOptions", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/datafeed.ts", + "lineNumber": 56 + }, + "signature": [ + "estypes.IndicesOptions" + ], + "initialIsOpen": false + }, + { + "id": "def-server.Job", + "type": "Type", + "label": "Job", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", + "lineNumber": 23 + }, + "signature": [ + "estypes.Job" + ], + "initialIsOpen": false + }, { "id": "def-server.JobId", "type": "Type", @@ -5426,7 +4350,7 @@ "description": [], "source": { "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", - "lineNumber": 11 + "lineNumber": 12 }, "signature": [ "string" @@ -5444,7 +4368,8 @@ "lineNumber": 13 }, "signature": [ - "Job & JobStats" + "Job", + " & JobStats" ], "initialIsOpen": false }, @@ -5463,6 +4388,21 @@ ], "initialIsOpen": false }, + { + "id": "def-server.ModelPlotConfig", + "type": "Type", + "label": "ModelPlotConfig", + "tags": [], + "description": [], + "source": { + "path": "x-pack/plugins/ml/common/types/anomaly_detection_jobs/job.ts", + "lineNumber": 90 + }, + "signature": [ + "estypes.ModelPlotConfig" + ], + "initialIsOpen": false + }, { "id": "def-server.ModuleSetupPayload", "type": "Type", diff --git a/api_docs/observability.json b/api_docs/observability.json index 439fd18db6469..2128e27f0106f 100644 --- a/api_docs/observability.json +++ b/api_docs/observability.json @@ -474,7 +474,7 @@ "description": [], "source": { "path": "x-pack/plugins/observability/public/hooks/use_fetcher.tsx", - "lineNumber": 30 + "lineNumber": 31 } }, { @@ -487,7 +487,7 @@ "description": [], "source": { "path": "x-pack/plugins/observability/public/hooks/use_fetcher.tsx", - "lineNumber": 31 + "lineNumber": 32 } }, { @@ -505,7 +505,7 @@ "description": [], "source": { "path": "x-pack/plugins/observability/public/hooks/use_fetcher.tsx", - "lineNumber": 33 + "lineNumber": 34 }, "signature": [ "boolean | undefined" @@ -514,7 +514,7 @@ ], "source": { "path": "x-pack/plugins/observability/public/hooks/use_fetcher.tsx", - "lineNumber": 32 + "lineNumber": 33 } } ], @@ -522,7 +522,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/observability/public/hooks/use_fetcher.tsx", - "lineNumber": 29 + "lineNumber": 30 }, "initialIsOpen": false }, @@ -1359,6 +1359,136 @@ }, "initialIsOpen": false }, + { + "id": "def-public.ObservabilityPublicPluginsSetup", + "type": "Interface", + "label": "ObservabilityPublicPluginsSetup", + "description": [], + "tags": [], + "children": [ + { + "tags": [], + "id": "def-public.ObservabilityPublicPluginsSetup.data", + "type": "Object", + "label": "data", + "description": [], + "source": { + "path": "x-pack/plugins/observability/public/plugin.ts", + "lineNumber": 30 + }, + "signature": [ + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataPluginApi", + "section": "def-public.DataPublicPluginSetup", + "text": "DataPublicPluginSetup" + } + ] + }, + { + "tags": [], + "id": "def-public.ObservabilityPublicPluginsSetup.home", + "type": "Object", + "label": "home", + "description": [], + "source": { + "path": "x-pack/plugins/observability/public/plugin.ts", + "lineNumber": 31 + }, + "signature": [ + { + "pluginId": "home", + "scope": "public", + "docId": "kibHomePluginApi", + "section": "def-public.HomePublicPluginSetup", + "text": "HomePublicPluginSetup" + }, + " | undefined" + ] + } + ], + "source": { + "path": "x-pack/plugins/observability/public/plugin.ts", + "lineNumber": 29 + }, + "initialIsOpen": false + }, + { + "id": "def-public.ObservabilityPublicPluginsStart", + "type": "Interface", + "label": "ObservabilityPublicPluginsStart", + "description": [], + "tags": [], + "children": [ + { + "tags": [], + "id": "def-public.ObservabilityPublicPluginsStart.home", + "type": "Object", + "label": "home", + "description": [], + "source": { + "path": "x-pack/plugins/observability/public/plugin.ts", + "lineNumber": 35 + }, + "signature": [ + { + "pluginId": "home", + "scope": "public", + "docId": "kibHomePluginApi", + "section": "def-public.HomePublicPluginStart", + "text": "HomePublicPluginStart" + }, + " | undefined" + ] + }, + { + "tags": [], + "id": "def-public.ObservabilityPublicPluginsStart.data", + "type": "Object", + "label": "data", + "description": [], + "source": { + "path": "x-pack/plugins/observability/public/plugin.ts", + "lineNumber": 36 + }, + "signature": [ + { + "pluginId": "data", + "scope": "public", + "docId": "kibDataPluginApi", + "section": "def-public.DataPublicPluginStart", + "text": "DataPublicPluginStart" + } + ] + }, + { + "tags": [], + "id": "def-public.ObservabilityPublicPluginsStart.lens", + "type": "Object", + "label": "lens", + "description": [], + "source": { + "path": "x-pack/plugins/observability/public/plugin.ts", + "lineNumber": 37 + }, + "signature": [ + { + "pluginId": "lens", + "scope": "public", + "docId": "kibLensPluginApi", + "section": "def-public.LensPublicStart", + "text": "LensPublicStart" + } + ] + } + ], + "source": { + "path": "x-pack/plugins/observability/public/plugin.ts", + "lineNumber": 34 + }, + "initialIsOpen": false + }, { "id": "def-public.Series", "type": "Interface", @@ -1951,21 +2081,21 @@ ], "objects": [], "setup": { - "id": "def-public.ObservabilityPluginSetup", + "id": "def-public.ObservabilityPublicSetup", "type": "Interface", - "label": "ObservabilityPluginSetup", + "label": "ObservabilityPublicSetup", "description": [], "tags": [], "children": [ { "tags": [], - "id": "def-public.ObservabilityPluginSetup.dashboard", + "id": "def-public.ObservabilityPublicSetup.dashboard", "type": "Object", "label": "dashboard", "description": [], "source": { "path": "x-pack/plugins/observability/public/plugin.ts", - "lineNumber": 25 + "lineNumber": 26 }, "signature": [ "{ register: typeof ", @@ -1982,20 +2112,20 @@ ], "source": { "path": "x-pack/plugins/observability/public/plugin.ts", - "lineNumber": 24 + "lineNumber": 25 }, "lifecycle": "setup", "initialIsOpen": true }, "start": { - "id": "def-public.ObservabilityPluginStart", + "id": "def-public.ObservabilityPublicStart", "type": "Type", - "label": "ObservabilityPluginStart", + "label": "ObservabilityPublicStart", "tags": [], "description": [], "source": { "path": "x-pack/plugins/observability/public/plugin.ts", - "lineNumber": 33 + "lineNumber": 40 }, "signature": [ "void" @@ -2086,8 +2216,8 @@ "pluginId": "observability", "scope": "server", "docId": "kibObservabilityPluginApi", - "section": "def-server.MappingsDefinition", - "text": "MappingsDefinition" + "section": "def-server.Mappings", + "text": "Mappings" }, "; client: ", { @@ -2118,26 +2248,26 @@ "description": [], "source": { "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 32 + "lineNumber": 20 } }, { "tags": [], "id": "def-server.createOrUpdateIndex.{\n- index,\n mappings,\n client,\n logger,\n}.mappings", - "type": "Object", + "type": "CompoundType", "label": "mappings", "description": [], "source": { "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 33 + "lineNumber": 21 }, "signature": [ { "pluginId": "observability", "scope": "server", "docId": "kibObservabilityPluginApi", - "section": "def-server.MappingsDefinition", - "text": "MappingsDefinition" + "section": "def-server.Mappings", + "text": "Mappings" } ] }, @@ -2149,7 +2279,7 @@ "description": [], "source": { "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 34 + "lineNumber": 22 }, "signature": [ { @@ -2169,7 +2299,7 @@ "description": [], "source": { "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 35 + "lineNumber": 23 }, "signature": [ "Logger" @@ -2178,7 +2308,7 @@ ], "source": { "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 31 + "lineNumber": 19 } } ], @@ -2186,7 +2316,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 26 + "lineNumber": 14 }, "initialIsOpen": false }, @@ -2224,82 +2354,32 @@ "initialIsOpen": false } ], - "interfaces": [ + "interfaces": [], + "enums": [], + "misc": [ { - "id": "def-server.MappingsDefinition", - "type": "Interface", - "label": "MappingsDefinition", - "description": [], + "id": "def-server.Mappings", + "type": "Type", + "label": "Mappings", "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.MappingsDefinition.dynamic", - "type": "CompoundType", - "label": "dynamic", - "description": [], - "source": { - "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 21 - }, - "signature": [ - "boolean | \"strict\" | undefined" - ] - }, - { - "tags": [], - "id": "def-server.MappingsDefinition.properties", - "type": "Object", - "label": "properties", - "description": [], - "source": { - "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 22 - }, - "signature": [ - "Record" - ] - }, - { - "tags": [], - "id": "def-server.MappingsDefinition.dynamic_templates", - "type": "Array", - "label": "dynamic_templates", - "description": [], - "source": { - "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 23 - }, - "signature": [ - "any[] | undefined" - ] - } - ], + "description": [], "source": { "path": "x-pack/plugins/observability/server/utils/create_or_update_index.ts", - "lineNumber": 20 + "lineNumber": 11 }, + "signature": [ + "TypeMapping", + " & { all_field?: ", + "AllField", + " | undefined; date_detection?: boolean | undefined; dynamic?: boolean | \"strict\" | undefined; dynamic_date_formats?: string[] | undefined; dynamic_templates?: Record | Record[] | undefined; field_names_field?: ", + "FieldNamesField" + ], "initialIsOpen": false - } - ], - "enums": [], - "misc": [ + }, { "id": "def-server.ObservabilityConfig", "type": "Type", @@ -2330,7 +2410,9 @@ "Annotation", "; }>; getById: (getByIdParams: { id: string; }) => Promise<", "GetResponse", - ">; delete: (deleteParams: { id: string; }) => Promise>; }" + ">; delete: (deleteParams: { id: string; }) => Promise<", + "DeleteResponse", + ">; }" ], "initialIsOpen": false } diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 353e65b0fa080..ef0e952f5161c 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -42,9 +42,6 @@ import observabilityObj from './observability.json'; ### Classes -### Interfaces - - ### Consts, variables and types diff --git a/api_docs/reporting.json b/api_docs/reporting.json index 29d0d485452da..be0c963866038 100644 --- a/api_docs/reporting.json +++ b/api_docs/reporting.json @@ -1352,7 +1352,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">) => Promise<", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">) => Promise<", { "pluginId": "core", "scope": "server", @@ -1377,7 +1377,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ], "description": [], "source": { diff --git a/api_docs/security.json b/api_docs/security.json index 8e1214654a82f..938a043a555cf 100644 --- a/api_docs/security.json +++ b/api_docs/security.json @@ -982,7 +982,7 @@ "lineNumber": 109 }, "signature": [ - "{ type: string; reason: string; caused_by: { type: string; reason: string; }; }[] | undefined" + "{ type: string; reason: string; caused_by?: { type: string; reason: string; } | undefined; }[] | undefined" ] } ], diff --git a/api_docs/security_solution.json b/api_docs/security_solution.json index ae208eb4facc7..ae85541ba6bfd 100644 --- a/api_docs/security_solution.json +++ b/api_docs/security_solution.json @@ -52,7 +52,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 75 + "lineNumber": 79 } } ], @@ -60,7 +60,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 75 + "lineNumber": 79 } }, { @@ -144,7 +144,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 98 + "lineNumber": 103 } }, { @@ -163,7 +163,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 98 + "lineNumber": 103 } } ], @@ -171,7 +171,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 98 + "lineNumber": 103 } }, { @@ -215,7 +215,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 348 + "lineNumber": 353 } }, { @@ -234,7 +234,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 348 + "lineNumber": 353 } } ], @@ -242,7 +242,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 348 + "lineNumber": 353 } }, { @@ -258,13 +258,13 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 393 + "lineNumber": 398 } } ], "source": { "path": "x-pack/plugins/security_solution/public/plugin.tsx", - "lineNumber": 72 + "lineNumber": 75 }, "initialIsOpen": false } @@ -607,7 +607,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 338 + "lineNumber": 339 } }, { @@ -626,7 +626,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 338 + "lineNumber": 339 } } ], @@ -634,7 +634,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 338 + "lineNumber": 339 } }, { @@ -650,7 +650,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 404 + "lineNumber": 405 } } ], @@ -1415,9 +1415,7 @@ "signature": [ "Error & Partial<", "ResponseError", - ", ", - "Context", - ">>" + ", unknown>>" ], "description": [], "source": { @@ -1429,9 +1427,7 @@ "signature": [ "(err: Error & Partial<", "ResponseError", - ", ", - "Context", - ">>) => ", + ", unknown>>) => ", "OutputError" ], "description": [], diff --git a/api_docs/telemetry.json b/api_docs/telemetry.json index 2d0108158e7e3..043e126de3640 100644 --- a/api_docs/telemetry.json +++ b/api_docs/telemetry.json @@ -334,7 +334,7 @@ "signature": [ "({ esClient }: ", "StatsCollectionConfig", - ") => Promise<{ clusterUuid: any; }[]>" + ") => Promise<{ clusterUuid: string; }[]>" ], "description": [ "\nGet the cluster uuids from the connected cluster." @@ -369,7 +369,7 @@ "description": [], "source": { "path": "src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts", - "lineNumber": 59 + "lineNumber": 60 } }, { @@ -388,7 +388,7 @@ "description": [], "source": { "path": "src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts", - "lineNumber": 60 + "lineNumber": 61 } }, { @@ -407,7 +407,7 @@ "description": [], "source": { "path": "src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts", - "lineNumber": 61 + "lineNumber": 62 } } ], @@ -434,7 +434,7 @@ "label": "getLocalStats", "source": { "path": "src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts", - "lineNumber": 58 + "lineNumber": 59 }, "tags": [], "returnComment": [], @@ -453,7 +453,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">, uiSettingsClient: ", + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">, uiSettingsClient: ", { "pluginId": "core", "scope": "server", @@ -478,7 +478,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ], "description": [], "source": { @@ -694,7 +694,7 @@ "description": [], "source": { "path": "src/plugins/telemetry/server/telemetry_collection/get_local_stats.ts", - "lineNumber": 50 + "lineNumber": 51 }, "signature": [ "{ timestamp: string; cluster_uuid: string; cluster_name: string; version: string; cluster_stats: any; collection: string; stack_stats: { data: DataTelemetryPayload | undefined; kibana: { count: number; indices: number; os: {}; versions: { version: string; count: number; }[]; plugins: { [plugin: string]: any; }; } | undefined; }; }" diff --git a/api_docs/telemetry_collection_manager.json b/api_docs/telemetry_collection_manager.json index 09e4b884bf809..30d3bb76a43b2 100644 --- a/api_docs/telemetry_collection_manager.json +++ b/api_docs/telemetry_collection_manager.json @@ -105,7 +105,7 @@ "section": "def-server.SavedObjectsClient", "text": "SavedObjectsClient" }, - ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\">" + ", \"get\" | \"delete\" | \"create\" | \"find\" | \"update\" | \"bulkCreate\" | \"bulkGet\" | \"bulkUpdate\" | \"errors\" | \"checkConflicts\" | \"resolve\" | \"addToNamespaces\" | \"deleteFromNamespaces\" | \"removeReferencesTo\" | \"openPointInTimeForType\" | \"closePointInTime\" | \"createPointInTimeFinder\">" ] }, { diff --git a/api_docs/telemetry_collection_xpack.json b/api_docs/telemetry_collection_xpack.json index cf1b1a5998553..8110cd6f2d27e 100644 --- a/api_docs/telemetry_collection_xpack.json +++ b/api_docs/telemetry_collection_xpack.json @@ -11,167 +11,25 @@ "server": { "classes": [], "functions": [], - "interfaces": [ + "interfaces": [], + "enums": [], + "misc": [ { "id": "def-server.ESLicense", - "type": "Interface", + "type": "Type", "label": "ESLicense", - "description": [], "tags": [], - "children": [ - { - "tags": [], - "id": "def-server.ESLicense.status", - "type": "string", - "label": "status", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 12 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.uid", - "type": "string", - "label": "uid", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 13 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.hkey", - "type": "string", - "label": "hkey", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 14 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.type", - "type": "string", - "label": "type", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 15 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.issue_date", - "type": "string", - "label": "issue_date", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 16 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.issue_date_in_millis", - "type": "number", - "label": "issue_date_in_millis", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 17 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.expiry_date", - "type": "string", - "label": "expiry_date", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 18 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.expiry_date_in_millis", - "type": "number", - "label": "expiry_date_in_millis", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 19 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.max_nodes", - "type": "number", - "label": "max_nodes", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 20 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.issued_to", - "type": "string", - "label": "issued_to", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 21 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.issuer", - "type": "string", - "label": "issuer", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 22 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.start_date_in_millis", - "type": "number", - "label": "start_date_in_millis", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 23 - } - }, - { - "tags": [], - "id": "def-server.ESLicense.max_resource_units", - "type": "number", - "label": "max_resource_units", - "description": [], - "source": { - "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 24 - } - } - ], + "description": [], "source": { "path": "x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_license.ts", - "lineNumber": 11 + "lineNumber": 10 }, + "signature": [ + "estypes.LicenseInformation" + ], "initialIsOpen": false } ], - "enums": [], - "misc": [], "objects": [] }, "common": { diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index dbebc1cde59ab..058a9d3fcb460 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -13,6 +13,6 @@ import telemetryCollectionXpackObj from './telemetry_collection_xpack.json'; ## Server -### Interfaces - +### Consts, variables and types + diff --git a/api_docs/vis_type_timeseries.json b/api_docs/vis_type_timeseries.json index 6f349cb3dfb93..907ced500294a 100644 --- a/api_docs/vis_type_timeseries.json +++ b/api_docs/vis_type_timeseries.json @@ -37,7 +37,7 @@ { "pluginId": "data", "scope": "server", - "docId": "kibDataPluginApi", + "docId": "kibDataSearchPluginApi", "section": "def-server.DataRequestHandlerContext", "text": "DataRequestHandlerContext" }, diff --git a/docs/api/alerting.asciidoc b/docs/api/alerting.asciidoc new file mode 100644 index 0000000000000..ad2d358d17ba0 --- /dev/null +++ b/docs/api/alerting.asciidoc @@ -0,0 +1,47 @@ +[[alerting-apis]] +== Alerting APIs + +The following APIs are available for {kib} alerting. + +* <> to create a rule + +* <> to update the attributes for existing rules + +* <> to retrieve a single rule by ID + +* <> to permanently remove a rule + +* <> to retrieve a paginated set of rules by condition + +* <> to retrieve a list of rule types + +* <> to enable a single rule by ID + +* <> to disable a single rule by ID + +* <> to mute alert for a single rule by ID + +* <> to unmute alert for a single rule by ID + +* <> to mute all alerts for a single rule by ID + +* <> to unmute all alerts for a single rule by ID + +* <> to retrieve the health of the Alerting framework + +For deprecated APIs, refer to <>. + +include::alerting/create_rule.asciidoc[] +include::alerting/update_rule.asciidoc[] +include::alerting/get_rules.asciidoc[] +include::alerting/delete_rule.asciidoc[] +include::alerting/find_rules.asciidoc[] +include::alerting/list_rule_types.asciidoc[] +include::alerting/enable_rule.asciidoc[] +include::alerting/disable_rule.asciidoc[] +include::alerting/mute_all_alerts.asciidoc[] +include::alerting/mute_alert.asciidoc[] +include::alerting/unmute_all_alerts.asciidoc[] +include::alerting/unmute_alert.asciidoc[] +include::alerting/health.asciidoc[] +include::alerting/legacy/index.asciidoc[] diff --git a/docs/api/alerting/create_rule.asciidoc b/docs/api/alerting/create_rule.asciidoc new file mode 100644 index 0000000000000..01b6dfc40fcf6 --- /dev/null +++ b/docs/api/alerting/create_rule.asciidoc @@ -0,0 +1,196 @@ +[[create-rule-api]] +=== Create rule API +++++ +Create rule +++++ + +Create {kib} rules. + +[[create-rule-api-request]] +==== Request + +`POST :/api/alerting/rule/` + +`POST :/s//api/alerting/rule/` + +[[create-rule-api-path-params]] +==== Path parameters + +``:: + (Optional, string) Specifies a UUID v1 or v4 to use instead of a randomly generated ID. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[create-rule-api-request-body]] +==== Request body + +`name`:: + (Required, string) A name to reference and search. + +`tags`:: + (Optional, string array) A list of keywords to reference and search. + +`rule_type_id`:: + (Required, string) The ID of the rule type that you want to call when the rule is scheduled to run. + +`schedule`:: + (Required, object) The schedule specifying when this rule should be run, using one of the available schedule formats specified under ++ +._Schedule Formats_. +[%collapsible%open] +===== +A schedule is structured such that the key specifies the format you wish to use and its value specifies the schedule. + +We currently support the _Interval format_ which specifies the interval in seconds, minutes, hours or days at which the rule should execute. +Example: `{ interval: "10s" }`, `{ interval: "5m" }`, `{ interval: "1h" }`, `{ interval: "1d" }`. + +There are plans to support multiple other schedule formats in the near future. +===== + +`throttle`:: + (Optional, string) How often this rule should fire the same actions. This will prevent the rule from sending out the same notification over and over. For example, if a rule with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications during this period. + +`notify_when`:: + (Required, string) The condition for throttling the notification: `onActionGroupChange`, `onActiveAlert`, or `onThrottleInterval`. + +`enabled`:: + (Optional, boolean) Indicates if you want to run the rule on an interval basis after it is created. + +`consumer`:: + (Required, string) The name of the application that owns the rule. This name has to match the Kibana Feature name, as that dictates the required RBAC privileges. + +`params`:: + (Required, object) The parameters to pass to the rule type executor `params` value. This will also validate against the rule type params validator, if defined. + +`actions`:: + (Optional, object array) An array of the following action objects. ++ +.Properties of the action objects: +[%collapsible%open] +===== + `group`::: + (Required, string) Grouping actions is recommended for escalations for different types of alerts. If you don't need this, set this value to `default`. + + `id`::: + (Required, string) The ID of the connector saved object to execute. + + `params`::: + (Required, object) The map to the `params` that the <> will receive. ` params` are handled as Mustache templates and passed a default set of context. +===== + + +[[create-rule-api-request-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[create-rule-api-example]] +==== Example + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/alerting/rule -H 'kbn-xsrf: true' -H 'Content-Type: application/json' -d ' +{ + "params":{ + "aggType":"avg", + "termSize":6, + "thresholdComparator":">", + "timeWindowSize":5, + "timeWindowUnit":"m", + "groupBy":"top", + "threshold":[ + 1000 + ], + "index":[ + ".test-index" + ], + "timeField":"@timestamp", + "aggField":"sheet.version", + "termField":"name.keyword" + }, + "consumer":"alerts", + "rule_type_id":".index-threshold", + "schedule":{ + "interval":"1m" + }, + "actions":[ + { + "id":"dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2", + "group":"threshold met", + "params":{ + "level":"info", + "message":"alert '{{alertName}}' is active for group '{{context.group}}':\n\n- Value: {{context.value}}\n- Conditions Met: {{context.conditions}} over {{params.timeWindowSize}}{{params.timeWindowUnit}}\n- Timestamp: {{context.date}}" + } + } + ], + "tags":[ + "cpu" + ], + "notify_when":"onActionGroupChange", + "name":"my alert" +}' +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "id": "41893910-6bca-11eb-9e0d-85d233e3ee35", + "notify_when": "onActionGroupChange", + "params": { + "aggType": "avg", + "termSize": 6, + "thresholdComparator": ">", + "timeWindowSize": 5, + "timeWindowUnit": "m", + "groupBy": "top", + "threshold": [ + 1000 + ], + "index": [ + ".kibana" + ], + "timeField": "@timestamp", + "aggField": "sheet.version", + "termField": "name.keyword" + }, + "consumer": "alerts", + "rule_type_id": ".index-threshold", + "schedule": { + "interval": "1m" + }, + "actions": [ + { + "connector_type_id": ".server-log", + "group": "threshold met", + "params": { + "level": "info", + "message": "alert {{alertName}} is active for group {{context.group}}:\n\n- Value: {{context.value}}\n- Conditions Met: {{context.conditions}} over {{params.timeWindowSize}}{{params.timeWindowUnit}}\n- Timestamp: {{context.date}}" + }, + "id": "dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2" + } + ], + "tags": [ + "cpu" + ], + "name": "my alert", + "enabled": true, + "throttle": null, + "api_key_owner": "elastic", + "created_by": "elastic", + "updated_by": "elastic", + "mute_all": false, + "muted_alert_ids": [], + "updated_at": "2021-02-10T18:03:19.961Z", + "created_at": "2021-02-10T18:03:19.961Z", + "scheduled_task_id": "425b0800-6bca-11eb-9e0d-85d233e3ee35", + "execution_status": { + "last_execution_date": "2021-02-10T18:03:19.966Z", + "status": "pending" + } +} +-------------------------------------------------- diff --git a/docs/api/alerting/delete_rule.asciidoc b/docs/api/alerting/delete_rule.asciidoc new file mode 100644 index 0000000000000..29e642e04c9e2 --- /dev/null +++ b/docs/api/alerting/delete_rule.asciidoc @@ -0,0 +1,41 @@ +[[delete-rule-api]] +=== Delete rule API +++++ +Delete rule +++++ + +Permanently remove a rule. + +WARNING: Once you delete a rule, you cannot recover it. + +[[delete-rule-api-request]] +==== Request + +`DELETE :/api/alerting/rule/` + +`DELETE :/s//api/alerting/rule/` + +[[delete-rule-api-path-params]] +==== Path parameters + +`id`:: + (Required, string) The ID of the rule that you want to remove. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[delete-rule-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Delete a rule with ID: + +[source,sh] +-------------------------------------------------- +$ curl -X DELETE api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35 +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/disable_rule.asciidoc b/docs/api/alerting/disable_rule.asciidoc new file mode 100644 index 0000000000000..ce003335623ef --- /dev/null +++ b/docs/api/alerting/disable_rule.asciidoc @@ -0,0 +1,39 @@ +[[disable-rule-api]] +=== Disable rule API +++++ +Disable rule +++++ + +Disable a rule. + +[[disable-rule-api-request]] +==== Request + +`POST :/api/alerting/rule//_disable` + +`POST :/s//api/alerting/rule//_disable` + +[[disable-rule-api-path-params]] +==== Path parameters + +`id`:: + (Required, string) The ID of the rule that you want to disable. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[disable-rule-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Disable a rule with ID: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35/_disable +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/enable_rule.asciidoc b/docs/api/alerting/enable_rule.asciidoc new file mode 100644 index 0000000000000..60f18b3510904 --- /dev/null +++ b/docs/api/alerting/enable_rule.asciidoc @@ -0,0 +1,39 @@ +[[enable-rule-api]] +=== Enable rule API +++++ +Enable rule +++++ + +Enable a rule. + +[[enable-rule-api-request]] +==== Request + +`POST :/api/alerting/rule//_enable` + +`POST :/s//api/alerting/rule//_enable` + +[[enable-rule-api-path-params]] +==== Path parameters + +`id`:: + (Required, string) The ID of the rule that you want to enable. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[enable-rule-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Enable a rule with ID: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35/_enable +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/find_rules.asciidoc b/docs/api/alerting/find_rules.asciidoc new file mode 100644 index 0000000000000..2df8b3522725c --- /dev/null +++ b/docs/api/alerting/find_rules.asciidoc @@ -0,0 +1,127 @@ +[[find-rules-api]] +=== Find rules API +++++ +Find rules +++++ + +Retrieve a paginated set of rules based on condition. + +NOTE: As rules change in {kib}, the results on each page of the response also +change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data. + +[[find-rules-api-request]] +==== Request + +`GET :/api/alerting/rules/_find` + +`GET :/s//api/alerting/rules/_find` + +[[find-rules-api-path-params]] +==== Path parameters + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[find-rules-api-query-params]] +==== Query Parameters + +NOTE: Rule `params` are stored as a {ref}/flattened.html[flattened field type] and analyzed as keywords. + +`per_page`:: + (Optional, number) The number of rules to return per page. + +`page`:: + (Optional, number) The page number. + +`search`:: + (Optional, string) An Elasticsearch {ref}/query-dsl-simple-query-string-query.html[simple_query_string] query that filters the rules in the response. + +`default_search_operator`:: + (Optional, string) The operator to use for the `simple_query_string`. The default is 'OR'. + +`search_fields`:: + (Optional, array|string) The fields to perform the `simple_query_string` parsed query against. + +`fields`:: + (Optional, array|string) The fields to return in the `attributes` key of the response. + +`sort_field`:: + (Optional, string) Sorts the response. Could be a rule field returned in the `attributes` key of the response. + +`sort_order`:: + (Optional, string) Sort direction, either `asc` or `desc`. + +`has_reference`:: + (Optional, object) Filters the rules that have a relation with the reference objects with the specific "type" and "ID". + +`filter`:: + (Optional, string) A <> string that you filter with an attribute from your saved object. + It should look like savedObjectType.attributes.title: "myTitle". However, If you used a direct attribute of a saved object, such as `updatedAt`, + you will have to define your filter, for example, savedObjectType.updatedAt > 2018-12-22. + +[[find-rules-api-request-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Examples + +Find rules with names that start with `my`: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/alerting/rules/_find?search_fields=name&search=my* +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "page": 1, + "per_page": 10, + "total": 1, + "data": [ + { + "id": "0a037d60-6b62-11eb-9e0d-85d233e3ee35", + "notify_when": "onActionGroupChange", + "params": { + "aggType": "avg", + }, + "consumer": "alerts", + "rule_type_id": "test.rule.type", + "schedule": { + "interval": "1m" + }, + "actions": [], + "tags": [], + "name": "test rule", + "enabled": true, + "throttle": null, + "api_key_owner": "elastic", + "created_by": "elastic", + "updated_by": "elastic", + "mute_all": false, + "muted_alert_ids": [], + "updated_at": "2021-02-10T05:37:19.086Z", + "created_at": "2021-02-10T05:37:19.086Z", + "scheduled_task_id": "0b092d90-6b62-11eb-9e0d-85d233e3ee35", + "execution_status": { + "last_execution_date": "2021-02-10T17:55:14.262Z", + "status": "ok" + } + }, + ] +} +-------------------------------------------------- + +For parameters that accept multiple values (e.g. `fields`), repeat the +query parameter for each value: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/alerting/rules/_find?fields=id&fields=name +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/get_rules.asciidoc b/docs/api/alerting/get_rules.asciidoc new file mode 100644 index 0000000000000..1594ec1fb7ae6 --- /dev/null +++ b/docs/api/alerting/get_rules.asciidoc @@ -0,0 +1,75 @@ +[[get-rule-api]] +=== Get rule API +++++ +Get rule +++++ + +Retrieve a rule by ID. + +[[get-rule-api-request]] +==== Request + +`GET :/api/alerting/rule/` + +`GET :/s//api/alerting/rule/` + +[[get-rule-api-params]] +==== Path parameters + +`id`:: + (Required, string) The ID of the rule to retrieve. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[get-rule-api-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[get-rule-api-example]] +==== Example + +Retrieve the rule object with the ID `41893910-6bca-11eb-9e0d-85d233e3ee35`: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35 +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "id": "0a037d60-6b62-11eb-9e0d-85d233e3ee35", + "notify_when": "onActionGroupChange", + "params": { + "aggType": "avg", + }, + "consumer": "alerts", + "rule_type_id": "test.rule.type", + "schedule": { + "interval": "1m" + }, + "actions": [], + "tags": [], + "name": "test rule", + "enabled": true, + "throttle": null, + "api_key_owner": "elastic", + "created_by": "elastic", + "updated_by": "elastic", + "mute_all": false, + "muted_alert_ids": [], + "updated_at": "2021-02-10T05:37:19.086Z", + "created_at": "2021-02-10T05:37:19.086Z", + "scheduled_task_id": "0b092d90-6b62-11eb-9e0d-85d233e3ee35", + "execution_status": { + "last_execution_date": "2021-02-10T17:55:14.262Z", + "status": "ok" + } +} +-------------------------------------------------- diff --git a/docs/api/alerting/health.asciidoc b/docs/api/alerting/health.asciidoc new file mode 100644 index 0000000000000..1e6b9ce22a981 --- /dev/null +++ b/docs/api/alerting/health.asciidoc @@ -0,0 +1,93 @@ +[[get-alerting-framework-health-api]] +=== Get Alerting framework health API +++++ +Get Alerting framework health +++++ + +Retrieve the health status of the Alerting framework. + +[[get-alerting-framework-health-api-request]] +==== Request + +`GET :/api/alerting/_health` + +`GET :/s//api/alerting/_health` + +[[get-alerting-framework-health-api-params]] +==== Path parameters + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[get-alerting-framework-health-api-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[get-alerting-framework-health-api-example]] +==== Example + +Retrieve the health status of the Alerting framework: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/alerting/_health +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "is_sufficiently_secure":true, + "has_permanent_encryption_key":true, + "alerting_framework_health":{ + "decryption_health":{ + "status":"ok", + "timestamp":"2021-02-10T23:35:04.949Z" + }, + "execution_health":{ + "status":"ok", + "timestamp":"2021-02-10T23:35:04.949Z" + }, + "read_health":{ + "status":"ok", + "timestamp":"2021-02-10T23:35:04.949Z" + } + } +} +-------------------------------------------------- + +The health API response contains the following properties: + +[cols="2*<"] +|=== + +| `is_sufficiently_secure` +| Returns `false` if security is enabled, but TLS is not. + +| `has_permanent_encryption_key` +| Return the state `false` if Encrypted Saved Object plugin has not a permanent encryption Key. + +| `alerting_framework_health` +| This state property has three substates that identify the health of the alerting framework API: `decryption_health`, `execution_health`, and `read_health`. + +|=== + +`alerting_framework_health` consists of the following properties: + +[cols="2*<"] +|=== + +| `decryption_health` +| Returns the timestamp and status of the rule decryption: `ok`, `warn` or `error` . + +| `execution_health` +| Returns the timestamp and status of the rule execution: `ok`, `warn` or `error`. + +| `read_health` +| Returns the timestamp and status of the rule reading events: `ok`, `warn` or `error`. + +|=== diff --git a/docs/api/alerts/create.asciidoc b/docs/api/alerting/legacy/create.asciidoc similarity index 97% rename from docs/api/alerts/create.asciidoc rename to docs/api/alerting/legacy/create.asciidoc index c3e6d36813972..5c594d64a3f45 100644 --- a/docs/api/alerts/create.asciidoc +++ b/docs/api/alerting/legacy/create.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-create]] -=== Create alert API +=== Legacy create alert API ++++ -Create alert +Legacy create alert ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Create {kib} alerts. [[alerts-api-create-request]] diff --git a/docs/api/alerts/delete.asciidoc b/docs/api/alerting/legacy/delete.asciidoc similarity index 86% rename from docs/api/alerts/delete.asciidoc rename to docs/api/alerting/legacy/delete.asciidoc index 72dfd5e87336c..68851973cab5b 100644 --- a/docs/api/alerts/delete.asciidoc +++ b/docs/api/alerting/legacy/delete.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-delete]] -=== Delete alert API +=== Legacy delete alert API ++++ -Delete alert +Legacy delete alert ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Permanently remove an alert. WARNING: Once you delete an alert, you cannot recover it. diff --git a/docs/api/alerts/disable.asciidoc b/docs/api/alerting/legacy/disable.asciidoc similarity index 85% rename from docs/api/alerts/disable.asciidoc rename to docs/api/alerting/legacy/disable.asciidoc index 86c58c37c2ecd..56e06371570c2 100644 --- a/docs/api/alerts/disable.asciidoc +++ b/docs/api/alerting/legacy/disable.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-disable]] -=== Disable alert API +=== Legacy disable alert API ++++ -Disable alert +Legacy disable alert ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Disable an alert. [[alerts-api-disable-request]] diff --git a/docs/api/alerts/enable.asciidoc b/docs/api/alerting/legacy/enable.asciidoc similarity index 85% rename from docs/api/alerts/enable.asciidoc rename to docs/api/alerting/legacy/enable.asciidoc index de1a5f7985a38..913d96a84352b 100644 --- a/docs/api/alerts/enable.asciidoc +++ b/docs/api/alerting/legacy/enable.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-enable]] -=== Enable alert API +=== Legacy enable alert API ++++ -Enable alert +Legacy enable alert ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Enable an alert. [[alerts-api-enable-request]] diff --git a/docs/api/alerts/find.asciidoc b/docs/api/alerting/legacy/find.asciidoc similarity index 96% rename from docs/api/alerts/find.asciidoc rename to docs/api/alerting/legacy/find.asciidoc index cc66d4e0f4183..94d9bc425bd21 100644 --- a/docs/api/alerts/find.asciidoc +++ b/docs/api/alerting/legacy/find.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-find]] -=== Find alerts API +=== Legacy find alerts API ++++ -Find alerts +Legacy find alerts ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Retrieve a paginated set of alerts based on condition. NOTE: As alerts change in {kib}, the results on each page of the response also diff --git a/docs/api/alerts/get.asciidoc b/docs/api/alerting/legacy/get.asciidoc similarity index 92% rename from docs/api/alerts/get.asciidoc rename to docs/api/alerting/legacy/get.asciidoc index 433605e857332..f1014d18e8774 100644 --- a/docs/api/alerts/get.asciidoc +++ b/docs/api/alerting/legacy/get.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-get]] -=== Get alert API +=== Legacy get alert API ++++ -Get alert +Legacy get alert ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Retrieve an alert by ID. [[alerts-api-get-request]] diff --git a/docs/api/alerts/health.asciidoc b/docs/api/alerting/legacy/health.asciidoc similarity index 92% rename from docs/api/alerts/health.asciidoc rename to docs/api/alerting/legacy/health.asciidoc index b29e5def53384..b25307fb5efd1 100644 --- a/docs/api/alerts/health.asciidoc +++ b/docs/api/alerting/legacy/health.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-health]] -=== Get Alerting framework health API +=== Legacy get Alerting framework health API ++++ -Get Alerting framework health +Legacy get Alerting framework health ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Retrieve the health status of the Alerting framework. [[alerts-api-health-request]] diff --git a/docs/api/alerting/legacy/index.asciidoc b/docs/api/alerting/legacy/index.asciidoc new file mode 100644 index 0000000000000..cce2c378bdb58 --- /dev/null +++ b/docs/api/alerting/legacy/index.asciidoc @@ -0,0 +1,18 @@ +[[alerts-api]] +=== Deprecated 7.x APIs + +These APIs are deprecated and will be removed as of 8.0. + +include::create.asciidoc[leveloffset=+1] +include::delete.asciidoc[leveloffset=+1] +include::disable.asciidoc[leveloffset=+1] +include::enable.asciidoc[leveloffset=+1] +include::find.asciidoc[leveloffset=+1] +include::get.asciidoc[leveloffset=+1] +include::health.asciidoc[leveloffset=+1] +include::list.asciidoc[leveloffset=+1] +include::mute.asciidoc[leveloffset=+1] +include::mute_all.asciidoc[leveloffset=+1] +include::unmute.asciidoc[leveloffset=+1] +include::unmute_all.asciidoc[leveloffset=+1] +include::update.asciidoc[leveloffset=+1] \ No newline at end of file diff --git a/docs/api/alerts/list.asciidoc b/docs/api/alerting/legacy/list.asciidoc similarity index 96% rename from docs/api/alerts/list.asciidoc rename to docs/api/alerting/legacy/list.asciidoc index e180945accfd3..e9ef3bbc27cd9 100644 --- a/docs/api/alerts/list.asciidoc +++ b/docs/api/alerting/legacy/list.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-list]] -=== List alert types API +=== Legacy list alert types API ++++ -List all alert types API +Legacy list all alert types ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Retrieve a list of all alert types. [[alerts-api-list-request]] diff --git a/docs/api/alerts/mute.asciidoc b/docs/api/alerting/legacy/mute.asciidoc similarity index 87% rename from docs/api/alerts/mute.asciidoc rename to docs/api/alerting/legacy/mute.asciidoc index 84a2996b65838..dff42f5911e53 100644 --- a/docs/api/alerts/mute.asciidoc +++ b/docs/api/alerting/legacy/mute.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-mute]] -=== Mute alert instance API +=== Legacy mute alert instance API ++++ -Mute alert instance +Legacy mute alert instance ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Mute an alert instance. [[alerts-api-mute-request]] diff --git a/docs/api/alerts/mute_all.asciidoc b/docs/api/alerting/legacy/mute_all.asciidoc similarity index 83% rename from docs/api/alerts/mute_all.asciidoc rename to docs/api/alerting/legacy/mute_all.asciidoc index 02f41eb3b768e..df89fa15d1590 100644 --- a/docs/api/alerts/mute_all.asciidoc +++ b/docs/api/alerting/legacy/mute_all.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-mute-all]] -=== Mute all alert instances API +=== Legacy mute all alert instances API ++++ -Mute all alert instances +Legacy mute all alert instances ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Mute all alert instances. [[alerts-api-mute-all-request]] diff --git a/docs/api/alerts/unmute.asciidoc b/docs/api/alerting/legacy/unmute.asciidoc similarity index 87% rename from docs/api/alerts/unmute.asciidoc rename to docs/api/alerting/legacy/unmute.asciidoc index eb73bb539154f..0be7e40dc1a19 100644 --- a/docs/api/alerts/unmute.asciidoc +++ b/docs/api/alerting/legacy/unmute.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-unmute]] -=== Unmute alert instance API +=== Legacy unmute alert instance API ++++ -Unmute alert instance +Legacy unmute alert instance ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Unmute an alert instance. [[alerts-api-unmute-request]] diff --git a/docs/api/alerts/unmute_all.asciidoc b/docs/api/alerting/legacy/unmute_all.asciidoc similarity index 83% rename from docs/api/alerts/unmute_all.asciidoc rename to docs/api/alerting/legacy/unmute_all.asciidoc index a20a20fd8204a..8687c2d2fe8bb 100644 --- a/docs/api/alerts/unmute_all.asciidoc +++ b/docs/api/alerting/legacy/unmute_all.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-unmute-all]] -=== Unmute all alert instances API +=== Legacy unmute all alert instances API ++++ -Unmute all alert instances +Legacy unmute all alert instances ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Unmute all alert instances. [[alerts-api-unmute-all-request]] diff --git a/docs/api/alerts/update.asciidoc b/docs/api/alerting/legacy/update.asciidoc similarity index 96% rename from docs/api/alerts/update.asciidoc rename to docs/api/alerting/legacy/update.asciidoc index a0b147ed4a15d..bffdf26c31400 100644 --- a/docs/api/alerts/update.asciidoc +++ b/docs/api/alerting/legacy/update.asciidoc @@ -1,9 +1,11 @@ [[alerts-api-update]] -=== Update alert API +=== Legacy update alert API ++++ -Update alert +Legacy update alert ++++ +WARNING: Deprecated in 7.13.0. Use <> instead. + Update the attributes for an existing alert. [[alerts-api-update-request]] diff --git a/docs/api/alerting/list_rule_types.asciidoc b/docs/api/alerting/list_rule_types.asciidoc new file mode 100644 index 0000000000000..77ca8601a6e8b --- /dev/null +++ b/docs/api/alerting/list_rule_types.asciidoc @@ -0,0 +1,135 @@ +[[list-rule-types-api]] +=== List rule types API +++++ +List rule types +++++ + +Retrieve a list of alerting rule types. + +[[list-rule-types-api-request]] +==== Request + +`GET :/api/alerting/rule_types` + +`GET :/s//api/alerting/rule_types` + +[[list-rule-types-api-params]] +==== Path parameters + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[list-rule-types-api-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[list-rule-types-api-example]] +==== Example + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/alerting/rule_types +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +[ + { + "id":".index-threshold", + "name":"Index threshold", + "action_groups":[ + { + "id":"threshold met", + "name":"Threshold met" + }, + { + "id":"recovered", + "name":"Recovered" + } + ], + "recovery_action_group":{ + "id":"recovered", + "name":"Recovered" + }, + "default_action_group_id":"threshold met", + "action_variables":{ + "context":[ + { + "name":"message", + "description":"A pre-constructed message for the alert." + }, + ], + "state":[], + "params":[ + { + "name":"threshold", + "description":"An array of values to use as the threshold; 'between' and 'notBetween' require two values, the others require one." + }, + { + "name":"index", + "description":"index" + }, + ] + }, + "producer":"stackAlerts", + "minimum_license_required":"basic", + "enabled_in_license":true, + "authorized_consumers":{ + "alerts":{ + "read":true, + "all":true + }, + "stackAlerts":{ + "read":true, + "all":true + }, + "uptime":{ + "read":true, + "all":true + } + } + } +] +-------------------------------------------------- + +Each rule type contains the following properties: + +[cols="2*<"] +|=== + +| `name` +| The descriptive name of the rule type. + +| `id` +| The unique ID of the rule type. + +| `minimum_license_required` +| The license required to use the rule type. + +| `enabled_in_license` +| Whether the rule type is enabled or disabled based on the license. + +| `action_groups` +| An explicit list of groups for which the rule type can schedule actions, each with the action group's unique ID and human readable name. Rule `actions` validation will use this configuration to ensure that groups are valid. Use `kbn-i18n` to translate the names of the action group when registering the rule type. + +| `recovery_action_group` +| An action group to use when an alert goes from an active state, to an inactive one. Do not specify this action group under the `action_groups` property. If `recovery_action_group` is not specified, the default `recovered` action group is used. + +| `default_action_group_od` +| The default ID for the rule type group. + +| `action_variables` +| An explicit list of action variables that the rule type makes available via context and state in action parameter templates, and a short human readable description. The Rule UI will use this information to prompt users for these variables in action parameter editors. Use `kbn-i18n` to translate the descriptions. + +| `producer` +| The ID of the application producing this rule type. + +| `authorized_consumers` +| The list of the plugins IDs that have access to the rule type. + +|=== diff --git a/docs/api/alerting/mute_alert.asciidoc b/docs/api/alerting/mute_alert.asciidoc new file mode 100644 index 0000000000000..4ebf12d1ce10c --- /dev/null +++ b/docs/api/alerting/mute_alert.asciidoc @@ -0,0 +1,42 @@ +[[mute-alert-api]] +=== Mute alert API +++++ +Mute alert +++++ + +Mute an alert. + +[[mute-alert-api-request]] +==== Request + +`POST :/api/alerting/rule//alert//_mute` + +`POST :/s//api/alerting/rule//alert//_mute` + +[[mute-alert-api-path-params]] +==== Path parameters + +`rule_id`:: + (Required, string) The ID of the rule whose alert you want to mute. + +`alert_id`:: + (Required, string) The ID of the alert that you want to mute. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[mute-alert-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Mute alert with ID: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35/alert/dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2/_mute +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/mute_all_alerts.asciidoc b/docs/api/alerting/mute_all_alerts.asciidoc new file mode 100644 index 0000000000000..58b6b14f49b4f --- /dev/null +++ b/docs/api/alerting/mute_all_alerts.asciidoc @@ -0,0 +1,39 @@ +[[mute-all-alerts-api]] +=== Mute all alerts API +++++ +Mute all alerts +++++ + +Mute all alerts. + +[[mute-all-alerts-api-request]] +==== Request + +`POST :/api/alerting/rule//_mute_all` + +`POST :/s//api/alerting/rule//_mute_all` + +[[mute-all-alerts-api-path-params]] +==== Path parameters + +`id`:: + (Required, string) The ID of the rule whose alerts you want to mute. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[mute-all-alerts-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Mute all alerts with ID: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35/_mute_all +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/unmute_alert.asciidoc b/docs/api/alerting/unmute_alert.asciidoc new file mode 100644 index 0000000000000..6e8870bb2fdae --- /dev/null +++ b/docs/api/alerting/unmute_alert.asciidoc @@ -0,0 +1,42 @@ +[[unmute-alert-api]] +=== Unmute alert API +++++ +Unmute alert +++++ + +Unmute an alert. + +[[unmute-alert-api-request]] +==== Request + +`POST :/api/alerting/rule//alert//_unmute` + +`POST :/s//api/alerting/rule//alert//_unmute` + +[[unmute-alert-api-path-params]] +==== Path parameters + +`rule_id`:: + (Required, string) The ID of the rule whose alert you want to mute. + +`alert_id`:: + (Required, string) The ID of the alert that you want to unmute. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[unmute-alert-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Unmute alert with ID: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35/alert/dceeb5d0-6b41-11eb-802b-85b0c1bc8ba2/_unmute +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/unmute_all_alerts.asciidoc b/docs/api/alerting/unmute_all_alerts.asciidoc new file mode 100644 index 0000000000000..c429ca288ae79 --- /dev/null +++ b/docs/api/alerting/unmute_all_alerts.asciidoc @@ -0,0 +1,39 @@ +[[unmute-all-alerts-api]] +=== Unmute all alerts API +++++ +Unmute all alerts +++++ + +Unmute all alerts. + +[[unmute-all-alerts-api-all-request]] +==== Request + +`POST :/api/alerting/rule//_unmute_all` + +`POST :/s//api/alerting/rule//_unmute_all` + +[[unmute-all-alerts-api-path-params]] +==== Path parameters + +`id`:: + (Required, string) The ID of the rule whose alerts you want to unmute. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[unmute-all-alerts-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +==== Example + +Unmute all alerts with ID: + +[source,sh] +-------------------------------------------------- +$ curl -X POST api/alerting/rule/41893910-6bca-11eb-9e0d-85d233e3ee35/_unmute_all +-------------------------------------------------- +// KIBANA diff --git a/docs/api/alerting/update_rule.asciidoc b/docs/api/alerting/update_rule.asciidoc new file mode 100644 index 0000000000000..76c88a009be01 --- /dev/null +++ b/docs/api/alerting/update_rule.asciidoc @@ -0,0 +1,136 @@ +[[update-rule-api]] +=== Update rule API +++++ +Update rule +++++ + +Update the attributes for an existing rule. + +[[update-rule-api-request]] +==== Request + +`PUT :/api/alerting/rule/` + +`PUT :/s//api/alerting/rule/` + +[[update-rule-api-path-params]] +==== Path parameters + +`id`:: + (Required, string) The ID of the rule that you want to update. + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + +[[update-rule-api-request-body]] +==== Request body + +`name`:: + (Required, string) A name to reference and search. + +`tags`:: + (Optional, string array) A list of keywords to reference and search. + +`schedule`:: + (Required, object) When to run this rule. Use one of the available schedule formats. ++ +._Schedule Formats_. +[%collapsible%open] +===== +A schedule uses a key: value format. {kib} currently supports the _Interval format_ , which specifies the interval in seconds, minutes, hours, or days at which to execute the rule. + +Example: `{ interval: "10s" }`, `{ interval: "5m" }`, `{ interval: "1h" }`, `{ interval: "1d" }`. + +===== + +`throttle`:: + (Optional, string) How often this rule should fire the same actions. This will prevent the rule from sending out the same notification over and over. For example, if a rule with a `schedule` of 1 minute stays in a triggered state for 90 minutes, setting a `throttle` of `10m` or `1h` will prevent it from sending 90 notifications during this period. + +`notify_when`:: + (Required, string) The condition for throttling the notification: `onActionGroupChange`, `onActiveAlert`, or `onThrottleInterval`. + +`params`:: + (Required, object) The parameters to pass to the rule type executor `params` value. This will also validate against the rule type params validator, if defined. + +`actions`:: + (Optional, object array) An array of the following action objects. ++ +.Properties of the action objects: +[%collapsible%open] +===== + `group`::: + (Required, string) Grouping actions is recommended for escalations for different types of alerts. If you don't need this, set the value to `default`. + + `id`::: + (Required, string) The ID of the action that saved object executes. + + `params`::: + (Required, object) The map to the `params` that the <> will receive. `params` are handled as Mustache templates and passed a default set of context. +===== + + +[[update-rule-api-response-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[update-rule-api-example]] +==== Example + +Update a rule with ID `ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74` with a different name: + +[source,sh] +-------------------------------------------------- +$ curl -X PUT api/alerting/rule/ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74 + +{ + "notify_when": "onActionGroupChange", + "params": { + "aggType": "avg", + }, + "schedule": { + "interval": "1m" + }, + "actions": [], + "tags": [], + "name": "new name", + "throttle": null, +} +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "id": "ac4e6b90-6be7-11eb-ba0d-9b1c1f912d74", + "notify_when": "onActionGroupChange", + "params": { + "aggType": "avg", + }, + "consumer": "alerts", + "rule_type_id": "test.rule.type", + "schedule": { + "interval": "1m" + }, + "actions": [], + "tags": [], + "name": "new name", + "enabled": true, + "throttle": null, + "api_key_owner": "elastic", + "created_by": "elastic", + "updated_by": "elastic", + "mute_all": false, + "muted_alert_ids": [], + "updated_at": "2021-02-10T05:37:19.086Z", + "created_at": "2021-02-10T05:37:19.086Z", + "scheduled_task_id": "0b092d90-6b62-11eb-9e0d-85d233e3ee35", + "execution_status": { + "last_execution_date": "2021-02-10T17:55:14.262Z", + "status": "ok" + } +} +-------------------------------------------------- diff --git a/docs/api/alerts.asciidoc b/docs/api/alerts.asciidoc deleted file mode 100644 index a19c538bcb4d7..0000000000000 --- a/docs/api/alerts.asciidoc +++ /dev/null @@ -1,42 +0,0 @@ -[[alerts-api]] -== Alerts APIs - -The following APIs are available for managing {kib} alerts. - -* <> to create an alert - -* <> to update the attributes for existing alerts - -* <> to retrieve a single alert by ID - -* <> to permanently remove an alert - -* <> to retrieve a paginated set of alerts by condition - -* <> to retrieve a list of all alert types - -* <> to enable a single alert by ID - -* <> to disable a single alert by ID - -* <> to mute alert instances for a single alert by ID - -* <> to unmute alert instances for a single alert by ID - -* <> to unmute all alert instances for a single alert by ID - -* <> to retrieve the health of the alerts framework - -include::alerts/create.asciidoc[] -include::alerts/update.asciidoc[] -include::alerts/get.asciidoc[] -include::alerts/delete.asciidoc[] -include::alerts/find.asciidoc[] -include::alerts/list.asciidoc[] -include::alerts/enable.asciidoc[] -include::alerts/disable.asciidoc[] -include::alerts/mute_all.asciidoc[] -include::alerts/mute.asciidoc[] -include::alerts/unmute_all.asciidoc[] -include::alerts/unmute.asciidoc[] -include::alerts/health.asciidoc[] diff --git a/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md b/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md index 551cbe3c93750..395c26a6e4bf6 100644 --- a/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md +++ b/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md @@ -10,10 +10,10 @@ Set of helpers used to create `KibanaResponse` to form HTTP response on an incom ```typescript kibanaResponseFactory: { - custom: | Buffer | Error | Stream | { + custom: | Error | Buffer | { message: string | Error; attributes?: Record | undefined; - } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; + } | Stream | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md index 5f8966f0227ac..f6421d65bc551 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md @@ -7,7 +7,7 @@ Signature: ```typescript -protected handleSearchError(e: KibanaServerError | AbortError, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; +protected handleSearchError(e: KibanaServerError | AbortError, options?: ISearchOptions, isTimeout?: boolean): Error; ``` ## Parameters @@ -15,8 +15,8 @@ protected handleSearchError(e: KibanaServerError | AbortError, timeoutSignal: Ab | Parameter | Type | Description | | --- | --- | --- | | e | KibanaServerError | AbortError | | -| timeoutSignal | AbortSignal | | | options | ISearchOptions | | +| isTimeout | boolean | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index 2247813562dc7..9d18309fc07be 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -27,7 +27,7 @@ export declare class SearchInterceptor | Method | Modifiers | Description | | --- | --- | --- | | [getTimeoutMode()](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) | | | -| [handleSearchError(e, timeoutSignal, options)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | +| [handleSearchError(e, options, isTimeout)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | | [showError(e)](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) | | | diff --git a/docs/user/api.asciidoc b/docs/user/api.asciidoc index c41f3d8a829e4..6daf252c524dd 100644 --- a/docs/user/api.asciidoc +++ b/docs/user/api.asciidoc @@ -99,7 +99,7 @@ include::{kib-repo-dir}/api/spaces-management.asciidoc[] include::{kib-repo-dir}/api/role-management.asciidoc[] include::{kib-repo-dir}/api/session-management.asciidoc[] include::{kib-repo-dir}/api/saved-objects.asciidoc[] -include::{kib-repo-dir}/api/alerts.asciidoc[] +include::{kib-repo-dir}/api/alerting.asciidoc[] include::{kib-repo-dir}/api/actions-and-connectors.asciidoc[] include::{kib-repo-dir}/api/dashboard-api.asciidoc[] include::{kib-repo-dir}/api/logstash-configuration-management.asciidoc[] diff --git a/package.json b/package.json index 99591fdc1ea40..2654c433ac5fa 100644 --- a/package.json +++ b/package.json @@ -127,11 +127,13 @@ "@kbn/apm-utils": "link:packages/kbn-apm-utils", "@kbn/config": "link:packages/kbn-config", "@kbn/config-schema": "link:packages/kbn-config-schema", + "@kbn/crypto": "link:packages/kbn-crypto", "@kbn/i18n": "link:packages/kbn-i18n", "@kbn/interpreter": "link:packages/kbn-interpreter", "@kbn/legacy-logging": "link:packages/kbn-legacy-logging", "@kbn/logging": "link:packages/kbn-logging", "@kbn/monaco": "link:packages/kbn-monaco", + "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/std": "link:packages/kbn-std", "@kbn/tinymath": "link:packages/kbn-tinymath", "@kbn/ui-framework": "link:packages/kbn-ui-framework", @@ -451,6 +453,7 @@ "@jest/reporters": "^26.5.2", "@kbn/babel-code-parser": "link:packages/kbn-babel-code-parser", "@kbn/babel-preset": "link:packages/kbn-babel-preset", + "@kbn/cli-dev-mode": "link:packages/kbn-cli-dev-mode", "@kbn/dev-utils": "link:packages/kbn-dev-utils", "@kbn/docs-utils": "link:packages/kbn-docs-utils", "@kbn/es": "link:packages/kbn-es", diff --git a/src/dev/cli_dev_mode/README.md b/packages/kbn-cli-dev-mode/README.md similarity index 72% rename from src/dev/cli_dev_mode/README.md rename to packages/kbn-cli-dev-mode/README.md index 397017027a52f..6ce41249674ce 100644 --- a/src/dev/cli_dev_mode/README.md +++ b/packages/kbn-cli-dev-mode/README.md @@ -26,8 +26,12 @@ The `DevServer` object is responsible for everything related to running and rest The `Optimizer` object manages a `@kbn/optimizer` instance, adapting its configuration and logging to the data available to the CLI. -## `BasePathProxyServer` (currently passed from core) +## `BasePathProxyServer` -The `BasePathProxyServer` is passed to the `CliDevMode` from core when the dev mode is trigged by the `--dev` flag. This proxy injects a random three character base path in the URL that Kibana is served from to help ensure that Kibana features are written to adapt to custom base path configurations from users. +This proxy injects a random three character base path in the URL that Kibana is served from to help ensure that Kibana features +are written to adapt to custom base path configurations from users. -The basePathProxy also has another important job, ensuring that requests don't fail because the server is restarting and that the browser receives front-end assets containing all saved changes. We accomplish this by observing the ready state of the `Optimizer` and `DevServer` objects and pausing all requests through the proxy until both objects report that they aren't building/restarting based on recently saved changes. \ No newline at end of file +The basePathProxy also has another important job, ensuring that requests don't fail because the server is restarting and +that the browser receives front-end assets containing all saved changes. We accomplish this by observing the ready state of +the `Optimizer` and `DevServer` objects and pausing all requests through the proxy until both objects report that +they aren't building/restarting based on recently saved changes. \ No newline at end of file diff --git a/packages/kbn-cli-dev-mode/jest.config.js b/packages/kbn-cli-dev-mode/jest.config.js new file mode 100644 index 0000000000000..d04dc571ef2a0 --- /dev/null +++ b/packages/kbn-cli-dev-mode/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-cli-dev-mode'], +}; diff --git a/packages/kbn-cli-dev-mode/package.json b/packages/kbn-cli-dev-mode/package.json new file mode 100644 index 0000000000000..2ee9831e96084 --- /dev/null +++ b/packages/kbn-cli-dev-mode/package.json @@ -0,0 +1,26 @@ +{ + "name": "@kbn/cli-dev-mode", + "main": "./target/index.js", + "types": "./target/index.d.ts", + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "private": true, + "scripts": { + "build": "../../node_modules/.bin/tsc", + "kbn:bootstrap": "yarn build", + "kbn:watch": "yarn build --watch" + }, + "kibana": { + "devOnly": true + }, + "dependencies": { + "@kbn/config": "link:../kbn-config", + "@kbn/config-schema": "link:../kbn-config-schema", + "@kbn/logging": "link:../kbn-logging", + "@kbn/server-http-tools": "link:../kbn-server-http-tools", + "@kbn/optimizer": "link:../kbn-optimizer", + "@kbn/std": "link:../kbn-std", + "@kbn/dev-utils": "link:../kbn-dev-utils", + "@kbn/utils": "link:../kbn-utils" + } +} \ No newline at end of file diff --git a/packages/kbn-cli-dev-mode/src/base_path_proxy_server.test.ts b/packages/kbn-cli-dev-mode/src/base_path_proxy_server.test.ts new file mode 100644 index 0000000000000..c99485c273364 --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/base_path_proxy_server.test.ts @@ -0,0 +1,358 @@ +/* + * 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 { Server } from '@hapi/hapi'; +import { EMPTY } from 'rxjs'; +import supertest from 'supertest'; +import { + getServerOptions, + getListenerOptions, + createServer, + IHttpConfig, +} from '@kbn/server-http-tools'; +import { ByteSizeValue } from '@kbn/config-schema'; + +import { BasePathProxyServer, BasePathProxyServerOptions } from './base_path_proxy_server'; +import { DevConfig } from './config/dev_config'; +import { TestLog } from './log'; + +describe('BasePathProxyServer', () => { + let server: Server; + let proxyServer: BasePathProxyServer; + let logger: TestLog; + let config: IHttpConfig; + let basePath: string; + let proxySupertest: supertest.SuperTest; + + beforeEach(async () => { + logger = new TestLog(); + + config = { + host: '127.0.0.1', + port: 10012, + keepaliveTimeout: 1000, + socketTimeout: 1000, + cors: { + enabled: false, + allowCredentials: false, + allowOrigin: [], + }, + ssl: { enabled: false }, + maxPayload: new ByteSizeValue(1024), + }; + + const serverOptions = getServerOptions(config); + const listenerOptions = getListenerOptions(config); + server = createServer(serverOptions, listenerOptions); + + // setup and start the proxy server + const proxyConfig: IHttpConfig = { ...config, port: 10013 }; + const devConfig = new DevConfig({ basePathProxyTarget: config.port }); + proxyServer = new BasePathProxyServer(logger, proxyConfig, devConfig); + const options: BasePathProxyServerOptions = { + shouldRedirectFromOldBasePath: () => true, + delayUntil: () => EMPTY, + }; + await proxyServer.start(options); + + // set the base path or throw if for some unknown reason it is not setup + if (proxyServer.basePath == null) { + throw new Error('Invalid null base path, all tests will fail'); + } else { + basePath = proxyServer.basePath; + } + proxySupertest = supertest(`http://127.0.0.1:${proxyConfig.port}`); + }); + + afterEach(async () => { + await server.stop(); + await proxyServer.stop(); + jest.clearAllMocks(); + }); + + test('root URL will return a 302 redirect', async () => { + await proxySupertest.get('/').expect(302); + }); + + test('root URL will return a redirect location with exactly 3 characters that are a-z', async () => { + const res = await proxySupertest.get('/'); + const location = res.header.location; + expect(location).toMatch(/[a-z]{3}/); + }); + + test('forwards request with the correct path', async () => { + server.route({ + method: 'GET', + path: `${basePath}/foo/{test}`, + handler: (request, h) => { + return h.response(request.params.test); + }, + }); + await server.start(); + + await proxySupertest + .get(`${basePath}/foo/some-string`) + .expect(200) + .then((res) => { + expect(res.text).toBe('some-string'); + }); + }); + + test('forwards request with the correct query params', async () => { + server.route({ + method: 'GET', + path: `${basePath}/foo/`, + handler: (request, h) => { + return h.response(request.query); + }, + }); + + await server.start(); + + await proxySupertest + .get(`${basePath}/foo/?bar=test&quux=123`) + .expect(200) + .then((res) => { + expect(res.body).toEqual({ bar: 'test', quux: '123' }); + }); + }); + + test('forwards the request body', async () => { + server.route({ + method: 'POST', + path: `${basePath}/foo/`, + handler: (request, h) => { + return h.response(request.payload); + }, + }); + + await server.start(); + + await proxySupertest + .post(`${basePath}/foo/`) + .send({ + bar: 'test', + baz: 123, + }) + .expect(200) + .then((res) => { + expect(res.body).toEqual({ bar: 'test', baz: 123 }); + }); + }); + + test('returns the correct status code', async () => { + server.route({ + method: 'GET', + path: `${basePath}/foo/`, + handler: (request, h) => { + return h.response({ foo: 'bar' }).code(417); + }, + }); + + await server.start(); + + await proxySupertest + .get(`${basePath}/foo/`) + .expect(417) + .then((res) => { + expect(res.body).toEqual({ foo: 'bar' }); + }); + }); + + test('returns the response headers', async () => { + server.route({ + method: 'GET', + path: `${basePath}/foo/`, + handler: (request, h) => { + return h.response({ foo: 'bar' }).header('foo', 'bar'); + }, + }); + + await server.start(); + + await proxySupertest + .get(`${basePath}/foo/`) + .expect(200) + .then((res) => { + expect(res.get('foo')).toEqual('bar'); + }); + }); + + test('handles putting', async () => { + server.route({ + method: 'PUT', + path: `${basePath}/foo/`, + handler: (request, h) => { + return h.response(request.payload); + }, + }); + + await server.start(); + + await proxySupertest + .put(`${basePath}/foo/`) + .send({ + bar: 'test', + baz: 123, + }) + .expect(200) + .then((res) => { + expect(res.body).toEqual({ bar: 'test', baz: 123 }); + }); + }); + + test('handles deleting', async () => { + server.route({ + method: 'DELETE', + path: `${basePath}/foo/{test}`, + handler: (request, h) => { + return h.response(request.params.test); + }, + }); + await server.start(); + + await proxySupertest + .delete(`${basePath}/foo/some-string`) + .expect(200) + .then((res) => { + expect(res.text).toBe('some-string'); + }); + }); + + describe('with `basepath: /bar` and `rewriteBasePath: false`', () => { + beforeEach(async () => { + const configWithBasePath: IHttpConfig = { + ...config, + basePath: '/bar', + rewriteBasePath: false, + } as IHttpConfig; + + const serverOptions = getServerOptions(configWithBasePath); + const listenerOptions = getListenerOptions(configWithBasePath); + server = createServer(serverOptions, listenerOptions); + + server.route({ + method: 'GET', + path: `${basePath}/`, + handler: (request, h) => { + return h.response('value:/'); + }, + }); + server.route({ + method: 'GET', + path: `${basePath}/foo`, + handler: (request, h) => { + return h.response('value:/foo'); + }, + }); + + await server.start(); + }); + + test('/bar => 404', async () => { + await proxySupertest.get(`${basePath}/bar`).expect(404); + }); + + test('/bar/ => 404', async () => { + await proxySupertest.get(`${basePath}/bar/`).expect(404); + }); + + test('/bar/foo => 404', async () => { + await proxySupertest.get(`${basePath}/bar/foo`).expect(404); + }); + + test('/ => /', async () => { + await proxySupertest + .get(`${basePath}/`) + .expect(200) + .then((res) => { + expect(res.text).toBe('value:/'); + }); + }); + + test('/foo => /foo', async () => { + await proxySupertest + .get(`${basePath}/foo`) + .expect(200) + .then((res) => { + expect(res.text).toBe('value:/foo'); + }); + }); + }); + + describe('shouldRedirect', () => { + let proxyServerWithoutShouldRedirect: BasePathProxyServer; + let proxyWithoutShouldRedirectSupertest: supertest.SuperTest; + + beforeEach(async () => { + // setup and start a proxy server which does not use "shouldRedirectFromOldBasePath" + const proxyConfig: IHttpConfig = { ...config, port: 10004 }; + const devConfig = new DevConfig({ basePathProxyTarget: config.port }); + proxyServerWithoutShouldRedirect = new BasePathProxyServer(logger, proxyConfig, devConfig); + const options: Readonly = { + shouldRedirectFromOldBasePath: () => false, // Return false to not redirect + delayUntil: () => EMPTY, + }; + await proxyServerWithoutShouldRedirect.start(options); + proxyWithoutShouldRedirectSupertest = supertest(`http://127.0.0.1:${proxyConfig.port}`); + }); + + afterEach(async () => { + await proxyServerWithoutShouldRedirect.stop(); + }); + + test('it will do a redirect if it detects what looks like a stale or previously used base path', async () => { + const fakeBasePath = basePath !== 'abc' ? 'abc' : 'efg'; + const res = await proxySupertest.get(`/${fakeBasePath}`).expect(302); + const location = res.header.location; + expect(location).toEqual(`${basePath}/`); + }); + + test('it will NOT do a redirect if it detects what looks like a stale or previously used base path if we intentionally turn it off', async () => { + const fakeBasePath = basePath !== 'abc' ? 'abc' : 'efg'; + await proxyWithoutShouldRedirectSupertest.get(`/${fakeBasePath}`).expect(404); + }); + + test('it will NOT redirect if it detects a larger path than 3 characters', async () => { + await proxySupertest.get('/abcde').expect(404); + }); + + test('it will NOT redirect if it is not a GET verb', async () => { + const fakeBasePath = basePath !== 'abc' ? 'abc' : 'efg'; + await proxySupertest.put(`/${fakeBasePath}`).expect(404); + }); + }); + + describe('constructor option for sending in a custom basePath', () => { + let proxyServerWithFooBasePath: BasePathProxyServer; + let proxyWithFooBasePath: supertest.SuperTest; + + beforeEach(async () => { + // setup and start a proxy server which uses a basePath of "foo" + const proxyConfig = { ...config, port: 10004, basePath: '/foo' }; // <-- "foo" here in basePath + const devConfig = new DevConfig({ basePathProxyTarget: config.port }); + proxyServerWithFooBasePath = new BasePathProxyServer(logger, proxyConfig, devConfig); + const options: Readonly = { + shouldRedirectFromOldBasePath: () => true, + delayUntil: () => EMPTY, + }; + await proxyServerWithFooBasePath.start(options); + proxyWithFooBasePath = supertest(`http://127.0.0.1:${proxyConfig.port}`); + }); + + afterEach(async () => { + await proxyServerWithFooBasePath.stop(); + }); + + test('it will do a redirect to foo which is our passed in value for the configuration', async () => { + const res = await proxyWithFooBasePath.get('/bar').expect(302); + const location = res.header.location; + expect(location).toEqual('/foo/'); + }); + }); +}); diff --git a/src/core/server/http/base_path_proxy_server.ts b/packages/kbn-cli-dev-mode/src/base_path_proxy_server.ts similarity index 90% rename from src/core/server/http/base_path_proxy_server.ts rename to packages/kbn-cli-dev-mode/src/base_path_proxy_server.ts index a5ed027189393..40841c8327cc2 100644 --- a/src/core/server/http/base_path_proxy_server.ts +++ b/packages/kbn-cli-dev-mode/src/base_path_proxy_server.ts @@ -8,21 +8,21 @@ import Url from 'url'; import { Agent as HttpsAgent, ServerOptions as TlsOptions } from 'https'; - import apm from 'elastic-apm-node'; -import { ByteSizeValue } from '@kbn/config-schema'; import { Server, Request } from '@hapi/hapi'; import HapiProxy from '@hapi/h2o2'; import { sampleSize } from 'lodash'; import * as Rx from 'rxjs'; import { take } from 'rxjs/operators'; +import { ByteSizeValue } from '@kbn/config-schema'; +import { createServer, getListenerOptions, getServerOptions } from '@kbn/server-http-tools'; -import { DevConfig } from '../dev'; -import { Logger } from '../logging'; -import { HttpConfig } from './http_config'; -import { createServer, getListenerOptions, getServerOptions } from './http_tools'; +import { DevConfig, HttpConfig } from './config'; +import { Log } from './log'; +const ONE_GIGABYTE = 1024 * 1024 * 1024; const alphabet = 'abcdefghijklmnopqrztuvwxyz'.split(''); +const getRandomBasePath = () => sampleSize(alphabet, 3).join(''); export interface BasePathProxyServerOptions { shouldRedirectFromOldBasePath: (path: string) => boolean; @@ -30,9 +30,22 @@ export interface BasePathProxyServerOptions { } export class BasePathProxyServer { + private readonly httpConfig: HttpConfig; private server?: Server; private httpsAgent?: HttpsAgent; + constructor( + private readonly log: Log, + httpConfig: HttpConfig, + private readonly devConfig: DevConfig + ) { + this.httpConfig = { + ...httpConfig, + maxPayload: new ByteSizeValue(ONE_GIGABYTE), + basePath: httpConfig.basePath ?? `/${getRandomBasePath()}`, + }; + } + public get basePath() { return this.httpConfig.basePath; } @@ -49,21 +62,8 @@ export class BasePathProxyServer { return this.httpConfig.port; } - constructor( - private readonly log: Logger, - private readonly httpConfig: HttpConfig, - private readonly devConfig: DevConfig - ) { - const ONE_GIGABYTE = 1024 * 1024 * 1024; - httpConfig.maxPayload = new ByteSizeValue(ONE_GIGABYTE); - - if (!httpConfig.basePath) { - httpConfig.basePath = `/${sampleSize(alphabet, 3).join('')}`; - } - } - - public async start(options: Readonly) { - this.log.debug('starting basepath proxy server'); + public async start(options: BasePathProxyServerOptions) { + this.log.write('starting basepath proxy server'); const serverOptions = getServerOptions(this.httpConfig); const listenerOptions = getListenerOptions(this.httpConfig); @@ -88,7 +88,7 @@ export class BasePathProxyServer { await this.server.start(); - this.log.info( + this.log.write( `basepath proxy server running at ${Url.format({ host: this.server.info.uri, pathname: this.httpConfig.basePath, @@ -101,7 +101,7 @@ export class BasePathProxyServer { return; } - this.log.debug('stopping basepath proxy server'); + this.log.write('stopping basepath proxy server'); await this.server.stop(); this.server = undefined; diff --git a/packages/kbn-cli-dev-mode/src/bootstrap.ts b/packages/kbn-cli-dev-mode/src/bootstrap.ts new file mode 100644 index 0000000000000..86a276c64f1f5 --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/bootstrap.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { REPO_ROOT } from '@kbn/utils'; +import { CliArgs, Env, RawConfigAdapter } from '@kbn/config'; +import { CliDevMode } from './cli_dev_mode'; +import { CliLog } from './log'; +import { convertToLogger } from './log_adapter'; +import { loadConfig } from './config'; + +interface BootstrapArgs { + configs: string[]; + cliArgs: CliArgs; + applyConfigOverrides: RawConfigAdapter; +} + +export async function bootstrapDevMode({ configs, cliArgs, applyConfigOverrides }: BootstrapArgs) { + const log = new CliLog(!!cliArgs.quiet, !!cliArgs.silent); + + const env = Env.createDefault(REPO_ROOT, { + configs, + cliArgs, + }); + + const config = await loadConfig({ + env, + logger: convertToLogger(log), + rawConfigAdapter: applyConfigOverrides, + }); + + const cliDevMode = new CliDevMode({ + cliArgs, + config, + log, + }); + + await cliDevMode.start(); +} diff --git a/src/dev/cli_dev_mode/cli_dev_mode.test.ts b/packages/kbn-cli-dev-mode/src/cli_dev_mode.test.ts similarity index 79% rename from src/dev/cli_dev_mode/cli_dev_mode.test.ts rename to packages/kbn-cli-dev-mode/src/cli_dev_mode.test.ts index 9ace543a8929b..d5bafe7280bd9 100644 --- a/src/dev/cli_dev_mode/cli_dev_mode.test.ts +++ b/packages/kbn-cli-dev-mode/src/cli_dev_mode.test.ts @@ -7,16 +7,16 @@ */ import Path from 'path'; - +import * as Rx from 'rxjs'; import { REPO_ROOT, createAbsolutePathSerializer, createAnyInstanceSerializer, } from '@kbn/dev-utils'; -import * as Rx from 'rxjs'; import { TestLog } from './log'; -import { CliDevMode } from './cli_dev_mode'; +import { CliDevMode, SomeCliArgs } from './cli_dev_mode'; +import type { CliDevConfig } from './config'; expect.addSnapshotSerializer(createAbsolutePathSerializer()); expect.addSnapshotSerializer(createAnyInstanceSerializer(Rx.Observable, 'Rx.Observable')); @@ -31,6 +31,9 @@ const { Optimizer } = jest.requireMock('./optimizer'); jest.mock('./dev_server'); const { DevServer } = jest.requireMock('./dev_server'); +jest.mock('./base_path_proxy_server'); +const { BasePathProxyServer } = jest.requireMock('./base_path_proxy_server'); + jest.mock('@kbn/dev-utils/target/ci_stats_reporter'); const { CiStatsReporter } = jest.requireMock('@kbn/dev-utils/target/ci_stats_reporter'); @@ -41,13 +44,6 @@ jest.mock('./get_server_watch_paths', () => ({ })), })); -beforeEach(() => { - process.argv = ['node', './script', 'foo', 'bar', 'baz']; - jest.clearAllMocks(); -}); - -const log = new TestLog(); - const mockBasePathProxy = { targetPort: 9999, basePath: '/foo/bar', @@ -55,26 +51,53 @@ const mockBasePathProxy = { stop: jest.fn(), }; -const defaultOptions = { +let log: TestLog; + +beforeEach(() => { + process.argv = ['node', './script', 'foo', 'bar', 'baz']; + log = new TestLog(); + BasePathProxyServer.mockImplementation(() => mockBasePathProxy); +}); + +afterEach(() => { + jest.clearAllMocks(); + mockBasePathProxy.start.mockReset(); + mockBasePathProxy.stop.mockReset(); +}); + +const createCliArgs = (parts: Partial = {}): SomeCliArgs => ({ + basePath: false, cache: true, disableOptimizer: false, dist: true, oss: true, - pluginPaths: [], - pluginScanDirs: [Path.resolve(REPO_ROOT, 'src/plugins')], - quiet: false, - silent: false, runExamples: false, watch: true, - log, -}; + silent: false, + quiet: false, + ...parts, +}); -afterEach(() => { - log.messages.length = 0; +const createDevConfig = (parts: Partial = {}): CliDevConfig => ({ + plugins: { + pluginSearchPaths: [Path.resolve(REPO_ROOT, 'src/plugins')], + additionalPluginPaths: [], + }, + dev: { + basePathProxyTargetPort: 9000, + }, + http: {} as any, + ...parts, +}); + +const createOptions = ({ cliArgs = {} }: { cliArgs?: Partial } = {}) => ({ + cliArgs: createCliArgs(cliArgs), + config: createDevConfig(), + log, }); it('passes correct args to sub-classes', () => { - new CliDevMode(defaultOptions); + new CliDevMode(createOptions()); expect(DevServer.mock.calls).toMatchInlineSnapshot(` Array [ @@ -105,6 +128,9 @@ it('passes correct args to sub-classes', () => { "enabled": true, "oss": true, "pluginPaths": Array [], + "pluginScanDirs": Array [ + /src/plugins, + ], "quiet": false, "repoRoot": , "runExamples": false, @@ -131,33 +157,38 @@ it('passes correct args to sub-classes', () => { ], ] `); + + expect(BasePathProxyServer).not.toHaveBeenCalled(); + expect(log.messages).toMatchInlineSnapshot(`Array []`); }); it('disables the optimizer', () => { - new CliDevMode({ - ...defaultOptions, - disableOptimizer: true, - }); + new CliDevMode(createOptions({ cliArgs: { disableOptimizer: true } })); expect(Optimizer.mock.calls[0][0]).toHaveProperty('enabled', false); }); it('disables the watcher', () => { - new CliDevMode({ - ...defaultOptions, - watch: false, - }); + new CliDevMode(createOptions({ cliArgs: { watch: false } })); expect(Optimizer.mock.calls[0][0]).toHaveProperty('watch', false); expect(Watcher.mock.calls[0][0]).toHaveProperty('enabled', false); }); -it('overrides the basePath of the server when basePathProxy is defined', () => { - new CliDevMode({ - ...defaultOptions, - basePathProxy: mockBasePathProxy as any, - }); +it('enables the basePath proxy', () => { + new CliDevMode(createOptions({ cliArgs: { basePath: true } })); + + expect(BasePathProxyServer).toHaveBeenCalledTimes(1); + expect(BasePathProxyServer.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + , + Object {}, + Object { + "basePathProxyTargetPort": 9000, + }, + ] + `); expect(DevServer.mock.calls[0][0].argv).toMatchInlineSnapshot(` Array [ @@ -229,9 +260,7 @@ describe('#start()/#stop()', () => { }); it('logs a warning if basePathProxy is not passed', () => { - new CliDevMode({ - ...defaultOptions, - }).start(); + new CliDevMode(createOptions()).start(); expect(log.messages).toMatchInlineSnapshot(` Array [ @@ -261,16 +290,9 @@ describe('#start()/#stop()', () => { }); it('calls start on BasePathProxy if enabled', () => { - const basePathProxy: any = { - start: jest.fn(), - }; + new CliDevMode(createOptions({ cliArgs: { basePath: true } })).start(); - new CliDevMode({ - ...defaultOptions, - basePathProxy, - }).start(); - - expect(basePathProxy.start.mock.calls).toMatchInlineSnapshot(` + expect(mockBasePathProxy.start.mock.calls).toMatchInlineSnapshot(` Array [ Array [ Object { @@ -283,7 +305,7 @@ describe('#start()/#stop()', () => { }); it('subscribes to Optimizer#run$, Watcher#run$, and DevServer#run$', () => { - new CliDevMode(defaultOptions).start(); + new CliDevMode(createOptions()).start(); expect(optimizerRun$.observers).toHaveLength(1); expect(watcherRun$.observers).toHaveLength(1); @@ -291,10 +313,7 @@ describe('#start()/#stop()', () => { }); it('logs an error and exits the process if Optimizer#run$ errors', () => { - new CliDevMode({ - ...defaultOptions, - basePathProxy: mockBasePathProxy as any, - }).start(); + new CliDevMode(createOptions({ cliArgs: { basePath: true } })).start(); expect(processExitMock).not.toHaveBeenCalled(); optimizerRun$.error({ stack: 'Error: foo bar' }); @@ -319,10 +338,7 @@ describe('#start()/#stop()', () => { }); it('logs an error and exits the process if Watcher#run$ errors', () => { - new CliDevMode({ - ...defaultOptions, - basePathProxy: mockBasePathProxy as any, - }).start(); + new CliDevMode(createOptions({ cliArgs: { basePath: true } })).start(); expect(processExitMock).not.toHaveBeenCalled(); watcherRun$.error({ stack: 'Error: foo bar' }); @@ -347,10 +363,7 @@ describe('#start()/#stop()', () => { }); it('logs an error and exits the process if DevServer#run$ errors', () => { - new CliDevMode({ - ...defaultOptions, - basePathProxy: mockBasePathProxy as any, - }).start(); + new CliDevMode(createOptions({ cliArgs: { basePath: true } })).start(); expect(processExitMock).not.toHaveBeenCalled(); devServerRun$.error({ stack: 'Error: foo bar' }); @@ -376,10 +389,7 @@ describe('#start()/#stop()', () => { it('throws if start() has already been called', () => { expect(() => { - const devMode = new CliDevMode({ - ...defaultOptions, - basePathProxy: mockBasePathProxy as any, - }); + const devMode = new CliDevMode(createOptions({ cliArgs: { basePath: true } })); devMode.start(); devMode.start(); @@ -387,10 +397,7 @@ describe('#start()/#stop()', () => { }); it('unsubscribes from all observables and stops basePathProxy when stopped', () => { - const devMode = new CliDevMode({ - ...defaultOptions, - basePathProxy: mockBasePathProxy as any, - }); + const devMode = new CliDevMode(createOptions({ cliArgs: { basePath: true } })); devMode.start(); devMode.stop(); diff --git a/src/dev/cli_dev_mode/cli_dev_mode.ts b/packages/kbn-cli-dev-mode/src/cli_dev_mode.ts similarity index 83% rename from src/dev/cli_dev_mode/cli_dev_mode.ts rename to packages/kbn-cli-dev-mode/src/cli_dev_mode.ts index f4f95f20daeef..94dbcb9654e8a 100644 --- a/src/dev/cli_dev_mode/cli_dev_mode.ts +++ b/packages/kbn-cli-dev-mode/src/cli_dev_mode.ts @@ -7,8 +7,6 @@ */ import Path from 'path'; - -import { REPO_ROOT, CiStatsReporter } from '@kbn/dev-utils'; import * as Rx from 'rxjs'; import { map, @@ -20,24 +18,32 @@ import { switchMap, concatMap, } from 'rxjs/operators'; - -import { CliArgs } from '../../core/server/config'; -import { LegacyConfig } from '../../core/server/legacy'; -import { BasePathProxyServer } from '../../core/server/http'; +import { CliArgs } from '@kbn/config'; +import { REPO_ROOT, CiStatsReporter } from '@kbn/dev-utils'; import { Log, CliLog } from './log'; import { Optimizer } from './optimizer'; import { DevServer } from './dev_server'; import { Watcher } from './watcher'; +import { BasePathProxyServer } from './base_path_proxy_server'; import { shouldRedirectFromOldBasePath } from './should_redirect_from_old_base_path'; import { getServerWatchPaths } from './get_server_watch_paths'; +import { CliDevConfig } from './config'; // timeout where the server is allowed to exit gracefully const GRACEFUL_TIMEOUT = 5000; export type SomeCliArgs = Pick< CliArgs, - 'quiet' | 'silent' | 'disableOptimizer' | 'watch' | 'oss' | 'runExamples' | 'cache' | 'dist' + | 'quiet' + | 'silent' + | 'disableOptimizer' + | 'watch' + | 'oss' + | 'runExamples' + | 'cache' + | 'dist' + | 'basePath' >; export interface CliDevModeOptions { @@ -76,49 +82,28 @@ const firstAllTrue = (...sources: Array>) => * */ export class CliDevMode { - static fromCoreServices( - cliArgs: SomeCliArgs, - config: LegacyConfig, - basePathProxy?: BasePathProxyServer - ) { - new CliDevMode({ - quiet: !!cliArgs.quiet, - silent: !!cliArgs.silent, - cache: !!cliArgs.cache, - disableOptimizer: !!cliArgs.disableOptimizer, - dist: !!cliArgs.dist, - oss: !!cliArgs.oss, - runExamples: !!cliArgs.runExamples, - pluginPaths: config.get('plugins.paths'), - pluginScanDirs: config.get('plugins.scanDirs'), - watch: !!cliArgs.watch, - basePathProxy, - }).start(); - } private readonly log: Log; private readonly basePathProxy?: BasePathProxyServer; private readonly watcher: Watcher; private readonly devServer: DevServer; private readonly optimizer: Optimizer; private startTime?: number; - private subscription?: Rx.Subscription; - constructor(options: CliDevModeOptions) { - this.basePathProxy = options.basePathProxy; - this.log = options.log || new CliLog(!!options.quiet, !!options.silent); + constructor({ cliArgs, config, log }: { cliArgs: SomeCliArgs; config: CliDevConfig; log?: Log }) { + this.log = log || new CliLog(!!cliArgs.quiet, !!cliArgs.silent); + + if (cliArgs.basePath) { + this.basePathProxy = new BasePathProxyServer(this.log, config.http, config.dev); + } const { watchPaths, ignorePaths } = getServerWatchPaths({ - pluginPaths: options.pluginPaths ?? [], - pluginScanDirs: [ - ...(options.pluginScanDirs ?? []), - Path.resolve(REPO_ROOT, 'src/plugins'), - Path.resolve(REPO_ROOT, 'x-pack/plugins'), - ], + pluginPaths: config.plugins.additionalPluginPaths, + pluginScanDirs: config.plugins.pluginSearchPaths, }); this.watcher = new Watcher({ - enabled: !!options.watch, + enabled: !!cliArgs.watch, log: this.log, cwd: REPO_ROOT, paths: watchPaths, @@ -133,10 +118,10 @@ export class CliDevMode { script: Path.resolve(REPO_ROOT, 'scripts/kibana'), argv: [ ...process.argv.slice(2).filter((v) => v !== '--no-watch'), - ...(options.basePathProxy + ...(this.basePathProxy ? [ - `--server.port=${options.basePathProxy.targetPort}`, - `--server.basePath=${options.basePathProxy.basePath}`, + `--server.port=${this.basePathProxy.targetPort}`, + `--server.basePath=${this.basePathProxy.basePath}`, '--server.rewriteBasePath=true', ] : []), @@ -153,16 +138,17 @@ export class CliDevMode { }); this.optimizer = new Optimizer({ - enabled: !options.disableOptimizer, + enabled: !cliArgs.disableOptimizer, repoRoot: REPO_ROOT, - oss: options.oss, - pluginPaths: options.pluginPaths, - runExamples: options.runExamples, - cache: options.cache, - dist: options.dist, - quiet: options.quiet, - silent: options.silent, - watch: options.watch, + oss: cliArgs.oss, + pluginPaths: config.plugins.additionalPluginPaths, + pluginScanDirs: config.plugins.pluginSearchPaths, + runExamples: cliArgs.runExamples, + cache: cliArgs.cache, + dist: cliArgs.dist, + quiet: !!cliArgs.quiet, + silent: !!cliArgs.silent, + watch: cliArgs.watch, }); } diff --git a/packages/kbn-cli-dev-mode/src/config/dev_config.ts b/packages/kbn-cli-dev-mode/src/config/dev_config.ts new file mode 100644 index 0000000000000..ddb54bb8f3f7c --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/config/dev_config.ts @@ -0,0 +1,28 @@ +/* + * 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'; + +export const devConfigSchema = schema.object( + { + basePathProxyTarget: schema.number({ + defaultValue: 5603, + }), + }, + { unknowns: 'ignore' } +); + +export type DevConfigType = TypeOf; + +export class DevConfig { + public basePathProxyTargetPort: number; + + constructor(rawConfig: DevConfigType) { + this.basePathProxyTargetPort = rawConfig.basePathProxyTarget; + } +} diff --git a/packages/kbn-cli-dev-mode/src/config/http_config.ts b/packages/kbn-cli-dev-mode/src/config/http_config.ts new file mode 100644 index 0000000000000..34f208c28df68 --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/config/http_config.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { ByteSizeValue, schema, TypeOf } from '@kbn/config-schema'; +import { ICorsConfig, IHttpConfig, ISslConfig, SslConfig, sslSchema } from '@kbn/server-http-tools'; + +export const httpConfigSchema = schema.object( + { + host: schema.string({ + defaultValue: 'localhost', + hostname: true, + }), + basePath: schema.maybe(schema.string()), + port: schema.number({ + defaultValue: 5601, + }), + maxPayload: schema.byteSize({ + defaultValue: '1048576b', + }), + keepaliveTimeout: schema.number({ + defaultValue: 120000, + }), + socketTimeout: schema.number({ + defaultValue: 120000, + }), + cors: schema.object({ + enabled: schema.boolean({ defaultValue: false }), + allowCredentials: schema.boolean({ defaultValue: false }), + allowOrigin: schema.arrayOf(schema.string(), { + defaultValue: ['*'], + }), + }), + ssl: sslSchema, + }, + { unknowns: 'ignore' } +); + +export type HttpConfigType = TypeOf; + +export class HttpConfig implements IHttpConfig { + basePath?: string; + host: string; + port: number; + maxPayload: ByteSizeValue; + keepaliveTimeout: number; + socketTimeout: number; + cors: ICorsConfig; + ssl: ISslConfig; + + constructor(rawConfig: HttpConfigType) { + this.basePath = rawConfig.basePath; + this.host = rawConfig.host; + this.port = rawConfig.port; + this.maxPayload = rawConfig.maxPayload; + this.keepaliveTimeout = rawConfig.keepaliveTimeout; + this.socketTimeout = rawConfig.socketTimeout; + this.cors = rawConfig.cors; + this.ssl = new SslConfig(rawConfig.ssl); + } +} diff --git a/packages/kbn-cli-dev-mode/src/config/index.ts b/packages/kbn-cli-dev-mode/src/config/index.ts new file mode 100644 index 0000000000000..89f6d647ef4f5 --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/config/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { DevConfig } from './dev_config'; +export type { PluginsConfig } from './plugins_config'; +export type { HttpConfig } from './http_config'; +export type { CliDevConfig } from './types'; +export { loadConfig } from './load_config'; diff --git a/packages/kbn-cli-dev-mode/src/config/load_config.ts b/packages/kbn-cli-dev-mode/src/config/load_config.ts new file mode 100644 index 0000000000000..46129834ca2d9 --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/config/load_config.ts @@ -0,0 +1,44 @@ +/* + * 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 { Env, RawConfigService, ConfigService, RawConfigAdapter } from '@kbn/config'; +import { Logger } from '@kbn/logging'; +import { devConfigSchema, DevConfig, DevConfigType } from './dev_config'; +import { httpConfigSchema, HttpConfig, HttpConfigType } from './http_config'; +import { pluginsConfigSchema, PluginsConfig, PluginsConfigType } from './plugins_config'; +import { CliDevConfig } from './types'; + +export const loadConfig = async ({ + env, + logger, + rawConfigAdapter, +}: { + env: Env; + logger: Logger; + rawConfigAdapter: RawConfigAdapter; +}): Promise => { + const rawConfigService = new RawConfigService(env.configs, rawConfigAdapter); + rawConfigService.loadConfig(); + + const configService = new ConfigService(rawConfigService, env, logger); + configService.setSchema('dev', devConfigSchema); + configService.setSchema('plugins', pluginsConfigSchema); + configService.setSchema('http', httpConfigSchema); + + await configService.validate(); + + const devConfig = configService.atPathSync('dev'); + const pluginsConfig = configService.atPathSync('plugins'); + const httpConfig = configService.atPathSync('http'); + + return { + dev: new DevConfig(devConfig), + plugins: new PluginsConfig(pluginsConfig, env), + http: new HttpConfig(httpConfig), + }; +}; diff --git a/packages/kbn-cli-dev-mode/src/config/plugins_config.ts b/packages/kbn-cli-dev-mode/src/config/plugins_config.ts new file mode 100644 index 0000000000000..7c7fa8902edb3 --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/config/plugins_config.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { Env } from '@kbn/config'; + +export const pluginsConfigSchema = schema.object( + { + paths: schema.arrayOf(schema.string(), { defaultValue: [] }), + }, + { unknowns: 'ignore' } +); + +export type PluginsConfigType = TypeOf; + +/** @internal */ +export class PluginsConfig { + /** + * Defines directories that we should scan for the plugin subdirectories. + */ + public readonly pluginSearchPaths: string[]; + + /** + * Defines directories where an additional plugin exists. + */ + public readonly additionalPluginPaths: string[]; + + constructor(rawConfig: PluginsConfigType, env: Env) { + this.pluginSearchPaths = [...env.pluginSearchPaths]; + this.additionalPluginPaths = rawConfig.paths; + } +} diff --git a/packages/kbn-cli-dev-mode/src/config/types.ts b/packages/kbn-cli-dev-mode/src/config/types.ts new file mode 100644 index 0000000000000..017442e09bd0d --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/config/types.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { DevConfig } from './dev_config'; +import type { HttpConfig } from './http_config'; +import type { PluginsConfig } from './plugins_config'; + +export interface CliDevConfig { + dev: DevConfig; + http: HttpConfig; + plugins: PluginsConfig; +} diff --git a/src/dev/cli_dev_mode/dev_server.test.ts b/packages/kbn-cli-dev-mode/src/dev_server.test.ts similarity index 100% rename from src/dev/cli_dev_mode/dev_server.test.ts rename to packages/kbn-cli-dev-mode/src/dev_server.test.ts diff --git a/src/dev/cli_dev_mode/dev_server.ts b/packages/kbn-cli-dev-mode/src/dev_server.ts similarity index 100% rename from src/dev/cli_dev_mode/dev_server.ts rename to packages/kbn-cli-dev-mode/src/dev_server.ts diff --git a/src/dev/cli_dev_mode/get_active_inspect_flag.ts b/packages/kbn-cli-dev-mode/src/get_active_inspect_flag.ts similarity index 100% rename from src/dev/cli_dev_mode/get_active_inspect_flag.ts rename to packages/kbn-cli-dev-mode/src/get_active_inspect_flag.ts diff --git a/src/dev/cli_dev_mode/get_server_watch_paths.test.ts b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts similarity index 100% rename from src/dev/cli_dev_mode/get_server_watch_paths.test.ts rename to packages/kbn-cli-dev-mode/src/get_server_watch_paths.test.ts diff --git a/src/dev/cli_dev_mode/get_server_watch_paths.ts b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts similarity index 87% rename from src/dev/cli_dev_mode/get_server_watch_paths.ts rename to packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts index 46aa15659a513..53aa53b5aa63a 100644 --- a/src/dev/cli_dev_mode/get_server_watch_paths.ts +++ b/packages/kbn-cli-dev-mode/src/get_server_watch_paths.ts @@ -47,15 +47,7 @@ export function getServerWatchPaths({ pluginPaths, pluginScanDirs }: Options) { ...pluginScanDirs, ].map((path) => Path.resolve(path)) ) - ); - - for (const watchPath of watchPaths) { - if (!Fs.existsSync(fromRoot(watchPath))) { - throw new Error( - `A watch directory [${watchPath}] does not exist, which will cause chokidar to fail. Either make sure the directory exists or remove it as a watch source in the ClusterManger` - ); - } - } + ).filter((path) => Fs.existsSync(fromRoot(path))); const ignorePaths = [ /[\\\/](\..*|node_modules|bower_components|target|public|__[a-z0-9_]+__|coverage)([\\\/]|$)/, diff --git a/src/dev/cli_dev_mode/index.ts b/packages/kbn-cli-dev-mode/src/index.ts similarity index 86% rename from src/dev/cli_dev_mode/index.ts rename to packages/kbn-cli-dev-mode/src/index.ts index db46957504b11..98b52087f231a 100644 --- a/src/dev/cli_dev_mode/index.ts +++ b/packages/kbn-cli-dev-mode/src/index.ts @@ -6,5 +6,4 @@ * Side Public License, v 1. */ -export * from './cli_dev_mode'; -export * from './log'; +export { bootstrapDevMode } from './bootstrap'; diff --git a/src/dev/cli_dev_mode/log.ts b/packages/kbn-cli-dev-mode/src/log.ts similarity index 100% rename from src/dev/cli_dev_mode/log.ts rename to packages/kbn-cli-dev-mode/src/log.ts diff --git a/packages/kbn-cli-dev-mode/src/log_adapter.ts b/packages/kbn-cli-dev-mode/src/log_adapter.ts new file mode 100644 index 0000000000000..65161fcc56e0e --- /dev/null +++ b/packages/kbn-cli-dev-mode/src/log_adapter.ts @@ -0,0 +1,28 @@ +/* + * 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 { Logger } from '@kbn/logging'; +import { Log } from './log'; + +export const convertToLogger = (cliLog: Log): Logger => { + const getErrorMessage = (msgOrError: string | Error): string => { + return typeof msgOrError === 'string' ? msgOrError : msgOrError.message; + }; + + const adapter: Logger = { + trace: (message) => cliLog.write(message), + debug: (message) => cliLog.write(message), + info: (message) => cliLog.write(message), + warn: (msgOrError) => cliLog.warn('warning', getErrorMessage(msgOrError)), + error: (msgOrError) => cliLog.bad('error', getErrorMessage(msgOrError)), + fatal: (msgOrError) => cliLog.bad('fatal', getErrorMessage(msgOrError)), + log: (record) => cliLog.write(record.message), + get: () => adapter, + }; + return adapter; +}; diff --git a/src/dev/cli_dev_mode/optimizer.test.ts b/packages/kbn-cli-dev-mode/src/optimizer.test.ts similarity index 96% rename from src/dev/cli_dev_mode/optimizer.test.ts rename to packages/kbn-cli-dev-mode/src/optimizer.test.ts index 409ad1a455a57..c270a00329897 100644 --- a/src/dev/cli_dev_mode/optimizer.test.ts +++ b/packages/kbn-cli-dev-mode/src/optimizer.test.ts @@ -43,6 +43,7 @@ const defaultOptions: Options = { dist: true, oss: true, pluginPaths: ['/some/dir'], + pluginScanDirs: ['/some-scan-path'], quiet: true, silent: true, repoRoot: '/app', @@ -83,6 +84,7 @@ it('uses options to create valid OptimizerConfig', () => { runExamples: false, oss: false, pluginPaths: [], + pluginScanDirs: [], repoRoot: '/foo/bar', watch: false, }); @@ -99,6 +101,9 @@ it('uses options to create valid OptimizerConfig', () => { "pluginPaths": Array [ "/some/dir", ], + "pluginScanDirs": Array [ + "/some-scan-path", + ], "repoRoot": "/app", "watch": true, }, @@ -111,6 +116,7 @@ it('uses options to create valid OptimizerConfig', () => { "includeCoreBundle": true, "oss": false, "pluginPaths": Array [], + "pluginScanDirs": Array [], "repoRoot": "/foo/bar", "watch": false, }, diff --git a/src/dev/cli_dev_mode/optimizer.ts b/packages/kbn-cli-dev-mode/src/optimizer.ts similarity index 97% rename from src/dev/cli_dev_mode/optimizer.ts rename to packages/kbn-cli-dev-mode/src/optimizer.ts index 771da21e6151b..5e2f16fcf7daa 100644 --- a/src/dev/cli_dev_mode/optimizer.ts +++ b/packages/kbn-cli-dev-mode/src/optimizer.ts @@ -31,6 +31,7 @@ export interface Options { oss: boolean; runExamples: boolean; pluginPaths: string[]; + pluginScanDirs: string[]; writeLogTo?: Writable; } @@ -56,6 +57,7 @@ export class Optimizer { oss: options.oss, examples: options.runExamples, pluginPaths: options.pluginPaths, + pluginScanDirs: options.pluginScanDirs, }); const dim = Chalk.dim('np bld'); diff --git a/src/dev/cli_dev_mode/should_redirect_from_old_base_path.test.ts b/packages/kbn-cli-dev-mode/src/should_redirect_from_old_base_path.test.ts similarity index 100% rename from src/dev/cli_dev_mode/should_redirect_from_old_base_path.test.ts rename to packages/kbn-cli-dev-mode/src/should_redirect_from_old_base_path.test.ts diff --git a/src/dev/cli_dev_mode/should_redirect_from_old_base_path.ts b/packages/kbn-cli-dev-mode/src/should_redirect_from_old_base_path.ts similarity index 100% rename from src/dev/cli_dev_mode/should_redirect_from_old_base_path.ts rename to packages/kbn-cli-dev-mode/src/should_redirect_from_old_base_path.ts diff --git a/src/dev/cli_dev_mode/test_helpers.ts b/packages/kbn-cli-dev-mode/src/test_helpers.ts similarity index 100% rename from src/dev/cli_dev_mode/test_helpers.ts rename to packages/kbn-cli-dev-mode/src/test_helpers.ts diff --git a/src/dev/cli_dev_mode/using_server_process.ts b/packages/kbn-cli-dev-mode/src/using_server_process.ts similarity index 100% rename from src/dev/cli_dev_mode/using_server_process.ts rename to packages/kbn-cli-dev-mode/src/using_server_process.ts diff --git a/src/dev/cli_dev_mode/watcher.test.ts b/packages/kbn-cli-dev-mode/src/watcher.test.ts similarity index 100% rename from src/dev/cli_dev_mode/watcher.test.ts rename to packages/kbn-cli-dev-mode/src/watcher.test.ts diff --git a/src/dev/cli_dev_mode/watcher.ts b/packages/kbn-cli-dev-mode/src/watcher.ts similarity index 100% rename from src/dev/cli_dev_mode/watcher.ts rename to packages/kbn-cli-dev-mode/src/watcher.ts diff --git a/packages/kbn-cli-dev-mode/tsconfig.json b/packages/kbn-cli-dev-mode/tsconfig.json new file mode 100644 index 0000000000000..b2bdaf8ceea36 --- /dev/null +++ b/packages/kbn-cli-dev-mode/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "outDir": "./target", + "declarationMap": true, + "types": ["jest", "node"] + }, + "include": ["./src/**/*.ts"], + "exclude": ["target"] +} diff --git a/packages/kbn-config/src/__mocks__/env.ts b/packages/kbn-config/src/__mocks__/env.ts index e3b3106933f1e..6f05f8f1f5a45 100644 --- a/packages/kbn-config/src/__mocks__/env.ts +++ b/packages/kbn-config/src/__mocks__/env.ts @@ -30,6 +30,5 @@ export function getEnvOptions(options: DeepPartial = {}): EnvOptions runExamples: false, ...(options.cliArgs || {}), }, - isDevCliParent: options.isDevCliParent !== undefined ? options.isDevCliParent : false, }; } diff --git a/packages/kbn-config/src/__snapshots__/env.test.ts.snap b/packages/kbn-config/src/__snapshots__/env.test.ts.snap index fae14529a4af3..570ed948774cc 100644 --- a/packages/kbn-config/src/__snapshots__/env.test.ts.snap +++ b/packages/kbn-config/src/__snapshots__/env.test.ts.snap @@ -21,7 +21,6 @@ Env { "/some/other/path/some-kibana.yml", ], "homeDir": "/test/kibanaRoot", - "isDevCliParent": false, "logDir": "/test/kibanaRoot/log", "mode": Object { "dev": true, @@ -65,7 +64,6 @@ Env { "/some/other/path/some-kibana.yml", ], "homeDir": "/test/kibanaRoot", - "isDevCliParent": false, "logDir": "/test/kibanaRoot/log", "mode": Object { "dev": false, @@ -108,7 +106,6 @@ Env { "/test/cwd/config/kibana.yml", ], "homeDir": "/test/kibanaRoot", - "isDevCliParent": true, "logDir": "/test/kibanaRoot/log", "mode": Object { "dev": true, @@ -151,7 +148,6 @@ Env { "/some/other/path/some-kibana.yml", ], "homeDir": "/test/kibanaRoot", - "isDevCliParent": false, "logDir": "/test/kibanaRoot/log", "mode": Object { "dev": false, @@ -194,7 +190,6 @@ Env { "/some/other/path/some-kibana.yml", ], "homeDir": "/test/kibanaRoot", - "isDevCliParent": false, "logDir": "/test/kibanaRoot/log", "mode": Object { "dev": false, @@ -237,7 +232,6 @@ Env { "/some/other/path/some-kibana.yml", ], "homeDir": "/some/home/dir", - "isDevCliParent": false, "logDir": "/some/home/dir/log", "mode": Object { "dev": false, diff --git a/packages/kbn-config/src/env.test.ts b/packages/kbn-config/src/env.test.ts index 09d44f31cf8d5..b9e97514c2dff 100644 --- a/packages/kbn-config/src/env.test.ts +++ b/packages/kbn-config/src/env.test.ts @@ -36,7 +36,6 @@ test('correctly creates default environment in dev mode.', () => { REPO_ROOT, getEnvOptions({ configs: ['/test/cwd/config/kibana.yml'], - isDevCliParent: true, }) ); diff --git a/packages/kbn-config/src/env.ts b/packages/kbn-config/src/env.ts index b6ff5e3b5aab2..c4845ab429c57 100644 --- a/packages/kbn-config/src/env.ts +++ b/packages/kbn-config/src/env.ts @@ -15,7 +15,6 @@ import { PackageInfo, EnvironmentMode } from './types'; export interface EnvOptions { configs: string[]; cliArgs: CliArgs; - isDevCliParent: boolean; } /** @internal */ @@ -89,12 +88,6 @@ export class Env { */ public readonly configs: readonly string[]; - /** - * Indicates that this Kibana instance is running in the parent process of the dev cli. - * @internal - */ - public readonly isDevCliParent: boolean; - /** * @internal */ @@ -111,7 +104,6 @@ export class Env { this.cliArgs = Object.freeze(options.cliArgs); this.configs = Object.freeze(options.configs); - this.isDevCliParent = options.isDevCliParent; const isDevMode = this.cliArgs.dev || this.cliArgs.envName === 'development'; this.mode = Object.freeze({ diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts index 24f271c979f32..8b0bdb0befbfd 100644 --- a/packages/kbn-config/src/index.ts +++ b/packages/kbn-config/src/index.ts @@ -16,7 +16,12 @@ export { ConfigDeprecationWithContext, } from './deprecation'; -export { RawConfigurationProvider, RawConfigService, getConfigFromFiles } from './raw'; +export { + RawConfigurationProvider, + RawConfigService, + RawConfigAdapter, + getConfigFromFiles, +} from './raw'; export { ConfigService, IConfigService } from './config_service'; export { Config, ConfigPath, isConfigPath, hasConfigPathIntersection } from './config'; diff --git a/packages/kbn-config/src/raw/index.ts b/packages/kbn-config/src/raw/index.ts index 8f65e7877ba56..01ad83728aa08 100644 --- a/packages/kbn-config/src/raw/index.ts +++ b/packages/kbn-config/src/raw/index.ts @@ -6,5 +6,5 @@ * Side Public License, v 1. */ -export { RawConfigService, RawConfigurationProvider } from './raw_config_service'; +export { RawConfigService, RawConfigurationProvider, RawConfigAdapter } from './raw_config_service'; export { getConfigFromFiles } from './read_config'; diff --git a/packages/kbn-config/src/raw/raw_config_service.ts b/packages/kbn-config/src/raw/raw_config_service.ts index af901f2b3d28e..cce1132bebdb0 100644 --- a/packages/kbn-config/src/raw/raw_config_service.ts +++ b/packages/kbn-config/src/raw/raw_config_service.ts @@ -13,7 +13,7 @@ import typeDetect from 'type-detect'; import { getConfigFromFiles } from './read_config'; -type RawConfigAdapter = (rawConfig: Record) => Record; +export type RawConfigAdapter = (rawConfig: Record) => Record; export type RawConfigurationProvider = Pick; diff --git a/packages/kbn-crypto/README.md b/packages/kbn-crypto/README.md new file mode 100644 index 0000000000000..4404c22eba37c --- /dev/null +++ b/packages/kbn-crypto/README.md @@ -0,0 +1,3 @@ +# @kbn/crypto + +Crypto tools and utilities for Kibana \ No newline at end of file diff --git a/packages/kbn-crypto/jest.config.js b/packages/kbn-crypto/jest.config.js new file mode 100644 index 0000000000000..811b87e5ed0f6 --- /dev/null +++ b/packages/kbn-crypto/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-crypto'], +}; diff --git a/packages/kbn-crypto/package.json b/packages/kbn-crypto/package.json new file mode 100644 index 0000000000000..6c7b3f3b0c719 --- /dev/null +++ b/packages/kbn-crypto/package.json @@ -0,0 +1,16 @@ +{ + "name": "@kbn/crypto", + "version": "1.0.0", + "private": true, + "license": "SSPL-1.0 OR Elastic License 2.0", + "main": "./target/index.js", + "scripts": { + "build": "../../node_modules/.bin/tsc", + "kbn:bootstrap": "yarn build", + "kbn:watch": "yarn build --watch" + }, + "dependencies": {}, + "devDependencies": { + "@kbn/dev-utils": "link:../kbn-dev-utils" + } +} \ No newline at end of file diff --git a/src/core/server/utils/crypto/__fixtures__/README.md b/packages/kbn-crypto/src/__fixtures__/README.md similarity index 100% rename from src/core/server/utils/crypto/__fixtures__/README.md rename to packages/kbn-crypto/src/__fixtures__/README.md diff --git a/src/core/server/utils/crypto/__fixtures__/index.ts b/packages/kbn-crypto/src/__fixtures__/index.ts similarity index 100% rename from src/core/server/utils/crypto/__fixtures__/index.ts rename to packages/kbn-crypto/src/__fixtures__/index.ts diff --git a/src/core/server/utils/crypto/__fixtures__/no_ca.p12 b/packages/kbn-crypto/src/__fixtures__/no_ca.p12 similarity index 100% rename from src/core/server/utils/crypto/__fixtures__/no_ca.p12 rename to packages/kbn-crypto/src/__fixtures__/no_ca.p12 diff --git a/src/core/server/utils/crypto/__fixtures__/no_cert.p12 b/packages/kbn-crypto/src/__fixtures__/no_cert.p12 similarity index 100% rename from src/core/server/utils/crypto/__fixtures__/no_cert.p12 rename to packages/kbn-crypto/src/__fixtures__/no_cert.p12 diff --git a/src/core/server/utils/crypto/__fixtures__/no_key.p12 b/packages/kbn-crypto/src/__fixtures__/no_key.p12 similarity index 100% rename from src/core/server/utils/crypto/__fixtures__/no_key.p12 rename to packages/kbn-crypto/src/__fixtures__/no_key.p12 diff --git a/src/core/server/utils/crypto/__fixtures__/two_cas.p12 b/packages/kbn-crypto/src/__fixtures__/two_cas.p12 similarity index 100% rename from src/core/server/utils/crypto/__fixtures__/two_cas.p12 rename to packages/kbn-crypto/src/__fixtures__/two_cas.p12 diff --git a/src/core/server/utils/crypto/__fixtures__/two_keys.p12 b/packages/kbn-crypto/src/__fixtures__/two_keys.p12 similarity index 100% rename from src/core/server/utils/crypto/__fixtures__/two_keys.p12 rename to packages/kbn-crypto/src/__fixtures__/two_keys.p12 diff --git a/src/core/server/utils/crypto/index.ts b/packages/kbn-crypto/src/index.ts similarity index 100% rename from src/core/server/utils/crypto/index.ts rename to packages/kbn-crypto/src/index.ts diff --git a/src/core/server/utils/crypto/pkcs12.test.ts b/packages/kbn-crypto/src/pkcs12.test.ts similarity index 99% rename from src/core/server/utils/crypto/pkcs12.test.ts rename to packages/kbn-crypto/src/pkcs12.test.ts index 8c6e5bae3b9c1..ba8eb6554f7b8 100644 --- a/src/core/server/utils/crypto/pkcs12.test.ts +++ b/packages/kbn-crypto/src/pkcs12.test.ts @@ -18,7 +18,7 @@ import { import { NO_CA_PATH, NO_CERT_PATH, NO_KEY_PATH, TWO_CAS_PATH, TWO_KEYS_PATH } from './__fixtures__'; import { readFileSync } from 'fs'; -import { readPkcs12Keystore, Pkcs12ReadResult, readPkcs12Truststore } from './index'; +import { readPkcs12Keystore, Pkcs12ReadResult, readPkcs12Truststore } from './pkcs12'; const reformatPem = (pem: string) => { // ensure consistency in line endings when comparing two PEM files diff --git a/src/core/server/utils/crypto/pkcs12.ts b/packages/kbn-crypto/src/pkcs12.ts similarity index 100% rename from src/core/server/utils/crypto/pkcs12.ts rename to packages/kbn-crypto/src/pkcs12.ts diff --git a/src/core/server/utils/crypto/sha256.test.ts b/packages/kbn-crypto/src/sha256.test.ts similarity index 100% rename from src/core/server/utils/crypto/sha256.test.ts rename to packages/kbn-crypto/src/sha256.test.ts diff --git a/src/core/server/utils/crypto/sha256.ts b/packages/kbn-crypto/src/sha256.ts similarity index 100% rename from src/core/server/utils/crypto/sha256.ts rename to packages/kbn-crypto/src/sha256.ts diff --git a/packages/kbn-crypto/tsconfig.json b/packages/kbn-crypto/tsconfig.json new file mode 100644 index 0000000000000..e9dd6313e6f79 --- /dev/null +++ b/packages/kbn-crypto/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target", + "declaration": true, + "declarationMap": true + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index ffc8d7ea8c505..c175979f0e820 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -101,7 +101,7 @@ OptimizerConfig { } `; -exports[`prepares assets for distribution: bar bundle 1`] = `"!function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{enumerable:!0,get:getter})},__webpack_require__.r=function(exports){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(exports,\\"__esModule\\",{value:!0})},__webpack_require__.t=function(value,mode){if(1&mode&&(value=__webpack_require__(value)),8&mode)return value;if(4&mode&&\\"object\\"==typeof value&&value&&value.__esModule)return value;var ns=Object.create(null);if(__webpack_require__.r(ns),Object.defineProperty(ns,\\"default\\",{enumerable:!0,value:value}),2&mode&&\\"string\\"!=typeof value)for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function(){return module.default}:function(){return module};return __webpack_require__.d(getter,\\"a\\",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p=\\"\\",__webpack_require__(__webpack_require__.s=3)}([function(module,exports,__webpack_require__){\\"use strict\\";var memo,isOldIE=function(){return void 0===memo&&(memo=Boolean(window&&document&&document.all&&!window.atob)),memo},getTarget=function(){var memo={};return function(target){if(void 0===memo[target]){var styleTarget=document.querySelector(target);if(window.HTMLIFrameElement&&styleTarget instanceof window.HTMLIFrameElement)try{styleTarget=styleTarget.contentDocument.head}catch(e){styleTarget=null}memo[target]=styleTarget}return memo[target]}}(),stylesInDom=[];function getIndexByIdentifier(identifier){for(var result=-1,i=0;i/packages/kbn-server-http-tools'], +}; diff --git a/packages/kbn-server-http-tools/package.json b/packages/kbn-server-http-tools/package.json new file mode 100644 index 0000000000000..a8f99689f3335 --- /dev/null +++ b/packages/kbn-server-http-tools/package.json @@ -0,0 +1,20 @@ +{ + "name": "@kbn/server-http-tools", + "main": "./target/index.js", + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0", + "private": true, + "scripts": { + "build": "rm -rf target && ../../node_modules/.bin/tsc", + "kbn:bootstrap": "yarn build", + "kbn:watch": "yarn build --watch" + }, + "dependencies": { + "@kbn/config-schema": "link:../kbn-config-schema", + "@kbn/crypto": "link:../kbn-crypto", + "@kbn/std": "link:../kbn-std" + }, + "devDependencies": { + "@kbn/utility-types": "link:../kbn-utility-types" + } +} \ No newline at end of file diff --git a/packages/kbn-server-http-tools/src/create_server.ts b/packages/kbn-server-http-tools/src/create_server.ts new file mode 100644 index 0000000000000..4752e342d5d3e --- /dev/null +++ b/packages/kbn-server-http-tools/src/create_server.ts @@ -0,0 +1,29 @@ +/* + * 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 { Server, ServerOptions } from '@hapi/hapi'; +import { ListenerOptions } from './get_listener_options'; + +export function createServer(serverOptions: ServerOptions, listenerOptions: ListenerOptions) { + const server = new Server(serverOptions); + + server.listener.keepAliveTimeout = listenerOptions.keepaliveTimeout; + server.listener.setTimeout(listenerOptions.socketTimeout); + server.listener.on('timeout', (socket) => { + socket.destroy(); + }); + server.listener.on('clientError', (err, socket) => { + if (socket.writable) { + socket.end(Buffer.from('HTTP/1.1 400 Bad Request\r\n\r\n', 'ascii')); + } else { + socket.destroy(err); + } + }); + + return server; +} diff --git a/packages/kbn-server-http-tools/src/default_validation_error_handler.test.ts b/packages/kbn-server-http-tools/src/default_validation_error_handler.test.ts new file mode 100644 index 0000000000000..93b09ef13e030 --- /dev/null +++ b/packages/kbn-server-http-tools/src/default_validation_error_handler.test.ts @@ -0,0 +1,51 @@ +/* + * 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 Joi from 'joi'; +import { Request, ResponseToolkit } from '@hapi/hapi'; +import { + defaultValidationErrorHandler, + HapiValidationError, +} from './default_validation_error_handler'; + +const emptyOutput = { + statusCode: 400, + headers: {}, + payload: { + statusCode: 400, + error: '', + validation: { + source: '', + keys: [], + }, + }, +}; + +describe('defaultValidationErrorHandler', () => { + it('formats value validation errors correctly', () => { + expect.assertions(1); + const schema = Joi.array().items( + Joi.object({ + type: Joi.string().required(), + }).required() + ); + + const error = schema.validate([{}], { abortEarly: false }).error as HapiValidationError; + + // Emulate what Hapi v17 does by default + error.output = { ...emptyOutput }; + error.output.payload.validation.keys = ['0.type', '']; + + try { + defaultValidationErrorHandler({} as Request, {} as ResponseToolkit, error); + } catch (err) { + // Verify the empty string gets corrected to 'value' + expect(err.output.payload.validation.keys).toEqual(['0.type', 'value']); + } + }); +}); diff --git a/packages/kbn-server-http-tools/src/default_validation_error_handler.ts b/packages/kbn-server-http-tools/src/default_validation_error_handler.ts new file mode 100644 index 0000000000000..d2f4e993f3e4b --- /dev/null +++ b/packages/kbn-server-http-tools/src/default_validation_error_handler.ts @@ -0,0 +1,63 @@ +/* + * 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 { Lifecycle, Request, ResponseToolkit, Util } from '@hapi/hapi'; +import { ValidationError } from 'joi'; +import Hoek from '@hapi/hoek'; + +/** + * Hapi extends the ValidationError interface to add this output key with more data. + */ +export interface HapiValidationError extends ValidationError { + output: { + statusCode: number; + headers: Util.Dictionary; + payload: { + statusCode: number; + error: string; + message?: string; + validation: { + source: string; + keys: string[]; + }; + }; + }; +} + +/** + * Used to replicate Hapi v16 and below's validation responses. Should be used in the routes.validate.failAction key. + */ +export function defaultValidationErrorHandler( + request: Request, + h: ResponseToolkit, + err?: Error +): Lifecycle.ReturnValue { + // Newer versions of Joi don't format the key for missing params the same way. This shim + // provides backwards compatibility. Unfortunately, Joi doesn't export it's own Error class + // in JS so we have to rely on the `name` key before we can cast it. + // + // The Hapi code we're 'overwriting' can be found here: + // https://github.com/hapijs/hapi/blob/master/lib/validation.js#L102 + if (err && err.name === 'ValidationError' && err.hasOwnProperty('output')) { + const validationError: HapiValidationError = err as HapiValidationError; + const validationKeys: string[] = []; + + validationError.details.forEach((detail) => { + if (detail.path.length > 0) { + validationKeys.push(Hoek.escapeHtml(detail.path.join('.'))); + } else { + // If no path, use the value sigil to signal the entire value had an issue. + validationKeys.push('value'); + } + }); + + validationError.output.payload.validation.keys = validationKeys; + } + + throw err; +} diff --git a/packages/kbn-server-http-tools/src/get_listener_options.ts b/packages/kbn-server-http-tools/src/get_listener_options.ts new file mode 100644 index 0000000000000..00884312b599f --- /dev/null +++ b/packages/kbn-server-http-tools/src/get_listener_options.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { IHttpConfig } from './types'; + +export interface ListenerOptions { + keepaliveTimeout: number; + socketTimeout: number; +} + +export function getListenerOptions(config: IHttpConfig): ListenerOptions { + return { + keepaliveTimeout: config.keepaliveTimeout, + socketTimeout: config.socketTimeout, + }; +} diff --git a/packages/kbn-server-http-tools/src/get_request_id.test.ts b/packages/kbn-server-http-tools/src/get_request_id.test.ts new file mode 100644 index 0000000000000..1b098ed4842d3 --- /dev/null +++ b/packages/kbn-server-http-tools/src/get_request_id.test.ts @@ -0,0 +1,85 @@ +/* + * 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 { getRequestId } from './get_request_id'; + +jest.mock('uuid', () => ({ + v4: jest.fn().mockReturnValue('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'), +})); + +describe('getRequestId', () => { + describe('when allowFromAnyIp is true', () => { + it('generates a UUID if no x-opaque-id header is present', () => { + const request = { + headers: {}, + raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, + } as any; + expect(getRequestId(request, { allowFromAnyIp: true, ipAllowlist: [] })).toEqual( + 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + ); + }); + + it('uses x-opaque-id header value if present', () => { + const request = { + headers: { + 'x-opaque-id': 'id from header', + }, + raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, + } as any; + expect(getRequestId(request, { allowFromAnyIp: true, ipAllowlist: [] })).toEqual( + 'id from header' + ); + }); + }); + + describe('when allowFromAnyIp is false', () => { + describe('and ipAllowlist is empty', () => { + it('generates a UUID even if x-opaque-id header is present', () => { + const request = { + headers: { 'x-opaque-id': 'id from header' }, + raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, + } as any; + expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: [] })).toEqual( + 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + ); + }); + }); + + describe('and ipAllowlist is not empty', () => { + it('uses x-opaque-id header if request comes from trusted IP address', () => { + const request = { + headers: { 'x-opaque-id': 'id from header' }, + raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, + } as any; + expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: ['1.1.1.1'] })).toEqual( + 'id from header' + ); + }); + + it('generates a UUID if request comes from untrusted IP address', () => { + const request = { + headers: { 'x-opaque-id': 'id from header' }, + raw: { req: { socket: { remoteAddress: '5.5.5.5' } } }, + } as any; + expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: ['1.1.1.1'] })).toEqual( + 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + ); + }); + + it('generates UUID if request comes from trusted IP address but no x-opaque-id header is present', () => { + const request = { + headers: {}, + raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, + } as any; + expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: ['1.1.1.1'] })).toEqual( + 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' + ); + }); + }); + }); +}); diff --git a/packages/kbn-server-http-tools/src/get_request_id.ts b/packages/kbn-server-http-tools/src/get_request_id.ts new file mode 100644 index 0000000000000..3af70ecc3a7a3 --- /dev/null +++ b/packages/kbn-server-http-tools/src/get_request_id.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 { Request } from '@hapi/hapi'; +import uuid from 'uuid'; + +export function getRequestId( + request: Request, + { allowFromAnyIp, ipAllowlist }: { allowFromAnyIp: boolean; ipAllowlist: string[] } +): string { + const remoteAddress = request.raw.req.socket?.remoteAddress; + return allowFromAnyIp || + // socket may be undefined in integration tests that connect via the http listener directly + (remoteAddress && ipAllowlist.includes(remoteAddress)) + ? request.headers['x-opaque-id'] ?? uuid.v4() + : uuid.v4(); +} diff --git a/packages/kbn-server-http-tools/src/get_server_options.test.ts b/packages/kbn-server-http-tools/src/get_server_options.test.ts new file mode 100644 index 0000000000000..fdcc749f4ae9a --- /dev/null +++ b/packages/kbn-server-http-tools/src/get_server_options.test.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ByteSizeValue } from '@kbn/config-schema'; +import { getServerOptions } from './get_server_options'; +import { IHttpConfig } from './types'; + +jest.mock('fs', () => { + const original = jest.requireActual('fs'); + return { + // Hapi Inert patches native methods + ...original, + readFileSync: jest.fn(), + }; +}); + +const createConfig = (parts: Partial): IHttpConfig => ({ + host: 'localhost', + port: 5601, + socketTimeout: 120000, + keepaliveTimeout: 120000, + maxPayload: ByteSizeValue.parse('1048576b'), + ...parts, + cors: { + enabled: false, + allowCredentials: false, + allowOrigin: ['*'], + ...parts.cors, + }, + ssl: { + enabled: false, + ...parts.ssl, + }, +}); + +describe('getServerOptions', () => { + beforeEach(() => + jest.requireMock('fs').readFileSync.mockImplementation((path: string) => `content-${path}`) + ); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('properly configures TLS with default options', () => { + const httpConfig = createConfig({ + ssl: { + enabled: true, + key: 'some-key-path', + certificate: 'some-certificate-path', + }, + }); + + expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(` + Object { + "ca": undefined, + "cert": "some-certificate-path", + "ciphers": undefined, + "honorCipherOrder": true, + "key": "some-key-path", + "passphrase": undefined, + "rejectUnauthorized": undefined, + "requestCert": undefined, + "secureOptions": undefined, + } + `); + }); + + it('properly configures TLS with client authentication', () => { + const httpConfig = createConfig({ + ssl: { + enabled: true, + key: 'some-key-path', + certificate: 'some-certificate-path', + certificateAuthorities: ['ca-1', 'ca-2'], + cipherSuites: ['suite-a', 'suite-b'], + keyPassphrase: 'passPhrase', + rejectUnauthorized: true, + requestCert: true, + getSecureOptions: () => 42, + }, + }); + + expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(` + Object { + "ca": Array [ + "ca-1", + "ca-2", + ], + "cert": "some-certificate-path", + "ciphers": "suite-a:suite-b", + "honorCipherOrder": true, + "key": "some-key-path", + "passphrase": "passPhrase", + "rejectUnauthorized": true, + "requestCert": true, + "secureOptions": 42, + } + `); + }); + + it('properly configures CORS when cors enabled', () => { + const httpConfig = createConfig({ + cors: { + enabled: true, + allowCredentials: false, + allowOrigin: ['*'], + }, + }); + + expect(getServerOptions(httpConfig).routes?.cors).toEqual({ + credentials: false, + origin: ['*'], + headers: ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf'], + }); + }); +}); diff --git a/packages/kbn-server-http-tools/src/get_server_options.ts b/packages/kbn-server-http-tools/src/get_server_options.ts new file mode 100644 index 0000000000000..ade90a0e0d3f5 --- /dev/null +++ b/packages/kbn-server-http-tools/src/get_server_options.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 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 { RouteOptionsCors, ServerOptions } from '@hapi/hapi'; +import { ServerOptions as TLSOptions } from 'https'; +import { defaultValidationErrorHandler } from './default_validation_error_handler'; +import { IHttpConfig } from './types'; + +const corsAllowedHeaders = ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf']; + +/** + * Converts Kibana `HttpConfig` into `ServerOptions` that are accepted by the Hapi server. + */ +export function getServerOptions(config: IHttpConfig, { configureTLS = true } = {}) { + const cors: RouteOptionsCors | false = config.cors.enabled + ? { + credentials: config.cors.allowCredentials, + origin: config.cors.allowOrigin, + headers: corsAllowedHeaders, + } + : false; + const options: ServerOptions = { + host: config.host, + port: config.port, + routes: { + cache: { + privacy: 'private', + otherwise: 'private, no-cache, no-store, must-revalidate', + }, + cors, + payload: { + maxBytes: config.maxPayload.getValueInBytes(), + }, + validate: { + failAction: defaultValidationErrorHandler, + options: { + abortEarly: false, + }, + }, + }, + state: { + strictHeader: false, + isHttpOnly: true, + isSameSite: false, // necessary to allow using Kibana inside an iframe + }, + }; + + if (configureTLS && config.ssl.enabled) { + const ssl = config.ssl; + + // TODO: Hapi types have a typo in `tls` property type definition: `https.RequestOptions` is used instead of + // `https.ServerOptions`, and `honorCipherOrder` isn't presented in `https.RequestOptions`. + const tlsOptions: TLSOptions = { + ca: ssl.certificateAuthorities, + cert: ssl.certificate, + ciphers: config.ssl.cipherSuites?.join(':'), + // We use the server's cipher order rather than the client's to prevent the BEAST attack. + honorCipherOrder: true, + key: ssl.key, + passphrase: ssl.keyPassphrase, + secureOptions: ssl.getSecureOptions ? ssl.getSecureOptions() : undefined, + requestCert: ssl.requestCert, + rejectUnauthorized: ssl.rejectUnauthorized, + }; + + options.tls = tlsOptions; + } + + return options; +} diff --git a/packages/kbn-server-http-tools/src/index.ts b/packages/kbn-server-http-tools/src/index.ts new file mode 100644 index 0000000000000..bd1dffa0bb0ca --- /dev/null +++ b/packages/kbn-server-http-tools/src/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { IHttpConfig, ISslConfig, ICorsConfig } from './types'; +export { createServer } from './create_server'; +export { defaultValidationErrorHandler } from './default_validation_error_handler'; +export { getListenerOptions } from './get_listener_options'; +export { getServerOptions } from './get_server_options'; +export { getRequestId } from './get_request_id'; +export { sslSchema, SslConfig } from './ssl'; diff --git a/src/core/server/legacy/cli_dev_mode.js b/packages/kbn-server-http-tools/src/ssl/index.ts similarity index 86% rename from src/core/server/legacy/cli_dev_mode.js rename to packages/kbn-server-http-tools/src/ssl/index.ts index 3c4bdb4149780..cbc3f17f915ef 100644 --- a/src/core/server/legacy/cli_dev_mode.js +++ b/packages/kbn-server-http-tools/src/ssl/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { CliDevMode } from '../../../dev/cli_dev_mode'; +export { SslConfig, sslSchema } from './ssl_config'; diff --git a/src/core/server/http/ssl_config.test.mocks.ts b/packages/kbn-server-http-tools/src/ssl/ssl_config.test.mocks.ts similarity index 95% rename from src/core/server/http/ssl_config.test.mocks.ts rename to packages/kbn-server-http-tools/src/ssl/ssl_config.test.mocks.ts index 81dbcf55100f8..adc4adb76f804 100644 --- a/src/core/server/http/ssl_config.test.mocks.ts +++ b/packages/kbn-server-http-tools/src/ssl/ssl_config.test.mocks.ts @@ -13,7 +13,7 @@ jest.mock('fs', () => { export const mockReadPkcs12Keystore = jest.fn(); export const mockReadPkcs12Truststore = jest.fn(); -jest.mock('../utils', () => ({ +jest.mock('@kbn/crypto', () => ({ readPkcs12Keystore: mockReadPkcs12Keystore, readPkcs12Truststore: mockReadPkcs12Truststore, })); diff --git a/src/core/server/http/ssl_config.test.ts b/packages/kbn-server-http-tools/src/ssl/ssl_config.test.ts similarity index 99% rename from src/core/server/http/ssl_config.test.ts rename to packages/kbn-server-http-tools/src/ssl/ssl_config.test.ts index bb6b1c7ff29f3..112fcd8a449f7 100644 --- a/src/core/server/http/ssl_config.test.ts +++ b/packages/kbn-server-http-tools/src/ssl/ssl_config.test.ts @@ -34,7 +34,7 @@ describe('#SslConfig', () => { beforeEach(() => { const realFs = jest.requireActual('fs'); mockReadFileSync.mockImplementation((path: string) => realFs.readFileSync(path)); - const utils = jest.requireActual('../utils'); + const utils = jest.requireActual('@kbn/crypto'); mockReadPkcs12Keystore.mockImplementation((path: string, password?: string) => utils.readPkcs12Keystore(path, password) ); diff --git a/src/core/server/http/ssl_config.ts b/packages/kbn-server-http-tools/src/ssl/ssl_config.ts similarity index 93% rename from src/core/server/http/ssl_config.ts rename to packages/kbn-server-http-tools/src/ssl/ssl_config.ts index 917d416a77563..53d3616a09a75 100644 --- a/src/core/server/http/ssl_config.ts +++ b/packages/kbn-server-http-tools/src/ssl/ssl_config.ts @@ -7,9 +7,9 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; +import { readPkcs12Keystore, readPkcs12Truststore } from '@kbn/crypto'; import { constants as cryptoConstants } from 'crypto'; import { readFileSync } from 'fs'; -import { readPkcs12Keystore, readPkcs12Truststore } from '../utils'; const protocolMap = new Map([ ['TLSv1', cryptoConstants.SSL_OP_NO_TLSv1], @@ -81,14 +81,13 @@ type SslConfigType = TypeOf; export class SslConfig { public enabled: boolean; - public redirectHttpFromPort: number | undefined; - public key: string | undefined; - public certificate: string | undefined; - public certificateAuthorities: string[] | undefined; - public keyPassphrase: string | undefined; + public redirectHttpFromPort?: number; + public key?: string; + public certificate?: string; + public certificateAuthorities?: string[]; + public keyPassphrase?: string; public requestCert: boolean; public rejectUnauthorized: boolean; - public cipherSuites: string[]; public supportedProtocols: string[]; @@ -164,6 +163,4 @@ export class SslConfig { } } -const readFile = (file: string) => { - return readFileSync(file, 'utf8'); -}; +const readFile = (file: string) => readFileSync(file, 'utf8'); diff --git a/packages/kbn-server-http-tools/src/types.ts b/packages/kbn-server-http-tools/src/types.ts new file mode 100644 index 0000000000000..3cc117d542eee --- /dev/null +++ b/packages/kbn-server-http-tools/src/types.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { ByteSizeValue } from '@kbn/config-schema'; + +export interface IHttpConfig { + host: string; + port: number; + maxPayload: ByteSizeValue; + keepaliveTimeout: number; + socketTimeout: number; + cors: ICorsConfig; + ssl: ISslConfig; +} + +export interface ICorsConfig { + enabled: boolean; + allowCredentials: boolean; + allowOrigin: string[]; +} + +export interface ISslConfig { + enabled: boolean; + key?: string; + certificate?: string; + certificateAuthorities?: string[]; + cipherSuites?: string[]; + keyPassphrase?: string; + requestCert?: boolean; + rejectUnauthorized?: boolean; + getSecureOptions?: () => number; +} diff --git a/packages/kbn-server-http-tools/tsconfig.json b/packages/kbn-server-http-tools/tsconfig.json new file mode 100644 index 0000000000000..ec84b963aed70 --- /dev/null +++ b/packages/kbn-server-http-tools/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target", + "declaration": true, + "declarationMap": true + }, + "include": [ + "src/**/*" + ], + "dependencies": { + "@kbn/std": "link:../kbn-std" + } +} diff --git a/packages/kbn-utils/src/repo_root.ts b/packages/kbn-utils/src/repo_root.ts index 20a25023f4166..2c1617098fe20 100644 --- a/packages/kbn-utils/src/repo_root.ts +++ b/packages/kbn-utils/src/repo_root.ts @@ -57,3 +57,5 @@ const { kibanaDir, kibanaPkgJson } = findKibanaPackageJson(); export const REPO_ROOT = kibanaDir; export const UPSTREAM_BRANCH = kibanaPkgJson.branch; + +export const fromRoot = (...paths: string[]) => Path.resolve(REPO_ROOT, ...paths); diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 34b78bbd7e51e..86b4ac53841f7 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -12,10 +12,8 @@ import { statSync } from 'fs'; import { resolve } from 'path'; import url from 'url'; -import { getConfigPath } from '@kbn/utils'; +import { getConfigPath, fromRoot } from '@kbn/utils'; import { IS_KIBANA_DISTRIBUTABLE } from '../../legacy/utils'; -import { fromRoot } from '../../core/server/utils'; -import { bootstrap } from '../../core/server'; import { readKeystore } from '../keystore/read_keystore'; function canRequire(path) { @@ -31,9 +29,21 @@ function canRequire(path) { } } -const DEV_MODE_PATH = resolve(__dirname, '../../dev/cli_dev_mode'); +const DEV_MODE_PATH = '@kbn/cli-dev-mode'; const DEV_MODE_SUPPORTED = canRequire(DEV_MODE_PATH); +const getBootstrapScript = (isDev) => { + if (DEV_MODE_SUPPORTED && isDev && process.env.isDevCliChild !== 'true') { + // need dynamic require to exclude it from production build + // eslint-disable-next-line import/no-dynamic-require + const { bootstrapDevMode } = require(DEV_MODE_PATH); + return bootstrapDevMode; + } else { + const { bootstrap } = require('../../core/server'); + return bootstrap; + } +}; + const pathCollector = function () { const paths = []; return function (path) { @@ -79,6 +89,7 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { throw new Error(`Can't use --ssl when "${path}" configuration is already defined.`); } } + ensureNotDefined('server.ssl.certificate'); ensureNotDefined('server.ssl.key'); ensureNotDefined('server.ssl.keystore.path'); @@ -210,31 +221,40 @@ export default function (program) { } const unknownOptions = this.getUnknownOptions(); - await bootstrap({ - configs: [].concat(opts.config || []), - cliArgs: { - dev: !!opts.dev, - envName: unknownOptions.env ? unknownOptions.env.name : undefined, - // no longer supported - quiet: !!opts.quiet, - silent: !!opts.silent, - watch: !!opts.watch, - runExamples: !!opts.runExamples, - // We want to run without base path when the `--run-examples` flag is given so that we can use local - // links in other documentation sources, like "View this tutorial [here](http://localhost:5601/app/tutorial/xyz)". - // We can tell users they only have to run with `yarn start --run-examples` to get those - // local links to work. Similar to what we do for "View in Console" links in our - // elastic.co links. - basePath: opts.runExamples ? false : !!opts.basePath, - optimize: !!opts.optimize, - disableOptimizer: !opts.optimizer, - oss: !!opts.oss, - cache: !!opts.cache, - dist: !!opts.dist, - }, - features: { - isCliDevModeSupported: DEV_MODE_SUPPORTED, - }, + const configs = [].concat(opts.config || []); + const cliArgs = { + dev: !!opts.dev, + envName: unknownOptions.env ? unknownOptions.env.name : undefined, + // no longer supported + quiet: !!opts.quiet, + silent: !!opts.silent, + watch: !!opts.watch, + runExamples: !!opts.runExamples, + // We want to run without base path when the `--run-examples` flag is given so that we can use local + // links in other documentation sources, like "View this tutorial [here](http://localhost:5601/app/tutorial/xyz)". + // We can tell users they only have to run with `yarn start --run-examples` to get those + // local links to work. Similar to what we do for "View in Console" links in our + // elastic.co links. + basePath: opts.runExamples ? false : !!opts.basePath, + optimize: !!opts.optimize, + disableOptimizer: !opts.optimizer, + oss: !!opts.oss, + cache: !!opts.cache, + dist: !!opts.dist, + }; + + // In development mode, the main process uses the @kbn/dev-cli-mode + // bootstrap script instead of core's. The DevCliMode instance + // is in charge of starting up the optimizer, and spawning another + // `/script/kibana` process with the `isDevCliChild` varenv set to true. + // This variable is then used to identify that we're the 'real' + // Kibana server process, and will be using core's bootstrap script + // to effectively start Kibana. + const bootstrapScript = getBootstrapScript(cliArgs.dev); + + await bootstrapScript({ + configs, + cliArgs, applyConfigOverrides: (rawConfig) => applyConfigOverrides(rawConfig, opts, unknownOptions), }); }); diff --git a/src/core/server/bootstrap.ts b/src/core/server/bootstrap.ts index 42f6d9aedf1d6..4a07e0c010685 100644 --- a/src/core/server/bootstrap.ts +++ b/src/core/server/bootstrap.ts @@ -11,18 +11,10 @@ import { CliArgs, Env, RawConfigService } from './config'; import { Root } from './root'; import { CriticalError } from './errors'; -interface KibanaFeatures { - // Indicates whether we can run Kibana in dev mode in which Kibana is run as - // a child process together with optimizer "worker" processes that are - // orchestrated by a parent process (dev mode only feature). - isCliDevModeSupported: boolean; -} - interface BootstrapArgs { configs: string[]; cliArgs: CliArgs; applyConfigOverrides: (config: Record) => Record; - features: KibanaFeatures; } /** @@ -30,12 +22,7 @@ interface BootstrapArgs { * @internal * @param param0 - options */ -export async function bootstrap({ - configs, - cliArgs, - applyConfigOverrides, - features, -}: BootstrapArgs) { +export async function bootstrap({ configs, cliArgs, applyConfigOverrides }: BootstrapArgs) { if (cliArgs.optimize) { // --optimize is deprecated and does nothing now, avoid starting up and just shutdown return; @@ -52,7 +39,6 @@ export async function bootstrap({ const env = Env.createDefault(REPO_ROOT, { configs, cliArgs, - isDevCliParent: cliArgs.dev && features.isCliDevModeSupported && !process.env.isDevCliChild, }); const rawConfigService = new RawConfigService(env.configs, applyConfigOverrides); diff --git a/src/core/server/dev/dev_config.ts b/src/core/server/dev/dev_config.ts index 3a303a61c8563..2fec778d85713 100644 --- a/src/core/server/dev/dev_config.ts +++ b/src/core/server/dev/dev_config.ts @@ -6,26 +6,11 @@ * Side Public License, v 1. */ -import { schema, TypeOf } from '@kbn/config-schema'; +import { schema } from '@kbn/config-schema'; export const config = { path: 'dev', - schema: schema.object({ - basePathProxyTarget: schema.number({ - defaultValue: 5603, - }), - }), + // dev configuration is validated by the dev cli. + // we only need to register the `dev` schema to avoid failing core's config validation + schema: schema.object({}, { unknowns: 'ignore' }), }; - -export type DevConfigType = TypeOf; - -export class DevConfig { - public basePathProxyTargetPort: number; - - /** - * @internal - */ - constructor(rawConfig: DevConfigType) { - this.basePathProxyTargetPort = rawConfig.basePathProxyTarget; - } -} diff --git a/src/core/server/dev/index.ts b/src/core/server/dev/index.ts index 6e0fd343d2ec8..70257d2a5e6c5 100644 --- a/src/core/server/dev/index.ts +++ b/src/core/server/dev/index.ts @@ -6,5 +6,4 @@ * Side Public License, v 1. */ -export { config, DevConfig } from './dev_config'; -export type { DevConfigType } from './dev_config'; +export { config } from './dev_config'; diff --git a/src/core/server/elasticsearch/elasticsearch_config.test.mocks.ts b/src/core/server/elasticsearch/elasticsearch_config.test.mocks.ts index 32602849d2e45..63b2233b06a96 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.test.mocks.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.test.mocks.ts @@ -11,7 +11,7 @@ jest.mock('fs', () => ({ readFileSync: mockReadFileSync })); export const mockReadPkcs12Keystore = jest.fn(); export const mockReadPkcs12Truststore = jest.fn(); -jest.mock('../utils', () => ({ +jest.mock('@kbn/crypto', () => ({ readPkcs12Keystore: mockReadPkcs12Keystore, readPkcs12Truststore: mockReadPkcs12Truststore, })); diff --git a/src/core/server/elasticsearch/elasticsearch_config.test.ts b/src/core/server/elasticsearch/elasticsearch_config.test.ts index d3f9693bab229..4b6cf220ccd52 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.test.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.test.ts @@ -244,12 +244,12 @@ describe('throws when config is invalid', () => { beforeAll(() => { const realFs = jest.requireActual('fs'); mockReadFileSync.mockImplementation((path: string) => realFs.readFileSync(path)); - const utils = jest.requireActual('../utils'); + const crypto = jest.requireActual('@kbn/crypto'); mockReadPkcs12Keystore.mockImplementation((path: string, password?: string) => - utils.readPkcs12Keystore(path, password) + crypto.readPkcs12Keystore(path, password) ); mockReadPkcs12Truststore.mockImplementation((path: string, password?: string) => - utils.readPkcs12Truststore(path, password) + crypto.readPkcs12Truststore(path, password) ); }); diff --git a/src/core/server/elasticsearch/elasticsearch_config.ts b/src/core/server/elasticsearch/elasticsearch_config.ts index 879002a6ece51..d3432344f5a73 100644 --- a/src/core/server/elasticsearch/elasticsearch_config.ts +++ b/src/core/server/elasticsearch/elasticsearch_config.ts @@ -7,10 +7,10 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; +import { readPkcs12Keystore, readPkcs12Truststore } from '@kbn/crypto'; import { Duration } from 'moment'; import { readFileSync } from 'fs'; import { ConfigDeprecationProvider } from 'src/core/server'; -import { readPkcs12Keystore, readPkcs12Truststore } from '../utils'; import { ServiceConfigDescriptor } from '../internal_types'; import { getReservedHeaders } from './default_headers'; diff --git a/src/core/server/external_url/external_url_config.ts b/src/core/server/external_url/external_url_config.ts index 7e4afbfbfea05..da4e8199dc623 100644 --- a/src/core/server/external_url/external_url_config.ts +++ b/src/core/server/external_url/external_url_config.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { createSHA256Hash } from '../utils'; +import { createSHA256Hash } from '@kbn/crypto'; import { config } from './config'; const DEFAULT_CONFIG = Object.freeze(config.schema.validate({})); diff --git a/src/core/server/http/base_path_proxy_server.test.ts b/src/core/server/http/base_path_proxy_server.test.ts deleted file mode 100644 index 80c03a2af9031..0000000000000 --- a/src/core/server/http/base_path_proxy_server.test.ts +++ /dev/null @@ -1,1021 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { BasePathProxyServer, BasePathProxyServerOptions } from './base_path_proxy_server'; -import { loggingSystemMock } from '../logging/logging_system.mock'; -import { DevConfig } from '../dev/dev_config'; -import { EMPTY } from 'rxjs'; -import { HttpConfig } from './http_config'; -import { ByteSizeValue, schema } from '@kbn/config-schema'; -import { - KibanaRequest, - KibanaResponseFactory, - Router, - RouteValidationFunction, - RouteValidationResultFactory, -} from './router'; -import { HttpServer } from './http_server'; -import supertest from 'supertest'; -import { RequestHandlerContext } from 'kibana/server'; -import { readFileSync } from 'fs'; -import { KBN_CERT_PATH, KBN_KEY_PATH } from '@kbn/dev-utils'; -import { omit } from 'lodash'; -import { Readable } from 'stream'; - -/** - * Most of these tests are inspired by: - * src/core/server/http/http_server.test.ts - * and copied for completeness from that file. The modifications are that these tests use the developer proxy. - */ -describe('BasePathProxyServer', () => { - let server: HttpServer; - let proxyServer: BasePathProxyServer; - let config: HttpConfig; - let configWithSSL: HttpConfig; - let basePath: string; - let certificate: string; - let key: string; - let proxySupertest: supertest.SuperTest; - const logger = loggingSystemMock.createLogger(); - const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {}); - - beforeAll(() => { - certificate = readFileSync(KBN_CERT_PATH, 'utf8'); - key = readFileSync(KBN_KEY_PATH, 'utf8'); - }); - - beforeEach(async () => { - // setup the server but don't start it until each individual test so that routes can be dynamically configured per unit test. - server = new HttpServer(logger, 'tests'); - config = ({ - name: 'kibana', - host: '127.0.0.1', - port: 10012, - compression: { enabled: true }, - requestId: { - allowFromAnyIp: true, - ipAllowlist: [], - }, - autoListen: true, - keepaliveTimeout: 1000, - socketTimeout: 1000, - cors: { - enabled: false, - allowCredentials: false, - allowOrigin: [], - }, - ssl: { enabled: false }, - customResponseHeaders: {}, - maxPayload: new ByteSizeValue(1024), - rewriteBasePath: true, - } as unknown) as HttpConfig; - - configWithSSL = { - ...config, - ssl: { - enabled: true, - certificate, - cipherSuites: ['TLS_AES_256_GCM_SHA384'], - getSecureOptions: () => 0, - key, - redirectHttpFromPort: config.port + 1, - }, - } as HttpConfig; - - // setup and start the proxy server - const proxyConfig: HttpConfig = { ...config, port: 10013 }; - const devConfig = new DevConfig({ basePathProxyTarget: config.port }); - proxyServer = new BasePathProxyServer(logger, proxyConfig, devConfig); - const options: Readonly = { - shouldRedirectFromOldBasePath: () => true, - delayUntil: () => EMPTY, - }; - await proxyServer.start(options); - - // set the base path or throw if for some unknown reason it is not setup - if (proxyServer.basePath == null) { - throw new Error('Invalid null base path, all tests will fail'); - } else { - basePath = proxyServer.basePath; - } - proxySupertest = supertest(`http://127.0.0.1:${proxyConfig.port}`); - }); - - afterEach(async () => { - await server.stop(); - await proxyServer.stop(); - jest.clearAllMocks(); - }); - - test('root URL will return a 302 redirect', async () => { - await proxySupertest.get('/').expect(302); - }); - - test('root URL will return a redirect location with exactly 3 characters that are a-z', async () => { - const res = await proxySupertest.get('/'); - const location = res.header.location; - expect(location).toMatch(/[a-z]{3}/); - }); - - test('valid params', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - router.get( - { - path: '/{test}', - validate: { - params: schema.object({ - test: schema.string(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: req.params.test }); - } - ); - const { registerRouter } = await server.setup(config); - registerRouter(router); - await server.start(); - - await proxySupertest - .get(`${basePath}/foo/some-string`) - .expect(200) - .then((res) => { - expect(res.text).toBe('some-string'); - }); - }); - - test('invalid params', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.get( - { - path: '/{test}', - validate: { - params: schema.object({ - test: schema.number(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: String(req.params.test) }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .get(`${basePath}/foo/some-string`) - .expect(400) - .then((res) => { - expect(res.body).toEqual({ - error: 'Bad Request', - statusCode: 400, - message: '[request params.test]: expected value of type [number] but got [string]', - }); - }); - }); - - test('valid query', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.get( - { - path: '/', - validate: { - query: schema.object({ - bar: schema.string(), - quux: schema.number(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: req.query }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .get(`${basePath}/foo/?bar=test&quux=123`) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ bar: 'test', quux: 123 }); - }); - }); - - test('invalid query', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.get( - { - path: '/', - validate: { - query: schema.object({ - bar: schema.number(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: req.query }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .get(`${basePath}/foo/?bar=test`) - .expect(400) - .then((res) => { - expect(res.body).toEqual({ - error: 'Bad Request', - statusCode: 400, - message: '[request query.bar]: expected value of type [number] but got [string]', - }); - }); - }); - - test('valid body', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.post( - { - path: '/', - validate: { - body: schema.object({ - bar: schema.string(), - baz: schema.number(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: req.body }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .post(`${basePath}/foo/`) - .send({ - bar: 'test', - baz: 123, - }) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ bar: 'test', baz: 123 }); - }); - }); - - test('valid body with validate function', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.post( - { - path: '/', - validate: { - body: ({ bar, baz } = {}, { ok, badRequest }) => { - if (typeof bar === 'string' && typeof baz === 'number') { - return ok({ bar, baz }); - } else { - return badRequest('Wrong payload', ['body']); - } - }, - }, - }, - (_, req, res) => { - return res.ok({ body: req.body }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .post(`${basePath}/foo/`) - .send({ - bar: 'test', - baz: 123, - }) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ bar: 'test', baz: 123 }); - }); - }); - - test('not inline validation - specifying params', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - const bodyValidation = ( - { bar, baz }: any = {}, - { ok, badRequest }: RouteValidationResultFactory - ) => { - if (typeof bar === 'string' && typeof baz === 'number') { - return ok({ bar, baz }); - } else { - return badRequest('Wrong payload', ['body']); - } - }; - - router.post( - { - path: '/', - validate: { - body: bodyValidation, - }, - }, - (_, req, res) => { - return res.ok({ body: req.body }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .post(`${basePath}/foo/`) - .send({ - bar: 'test', - baz: 123, - }) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ bar: 'test', baz: 123 }); - }); - }); - - test('not inline validation - specifying validation handler', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - const bodyValidation: RouteValidationFunction<{ bar: string; baz: number }> = ( - { bar, baz } = {}, - { ok, badRequest } - ) => { - if (typeof bar === 'string' && typeof baz === 'number') { - return ok({ bar, baz }); - } else { - return badRequest('Wrong payload', ['body']); - } - }; - - router.post( - { - path: '/', - validate: { - body: bodyValidation, - }, - }, - (_, req, res) => { - return res.ok({ body: req.body }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .post(`${basePath}/foo/`) - .send({ - bar: 'test', - baz: 123, - }) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ bar: 'test', baz: 123 }); - }); - }); - - test('not inline handler - KibanaRequest', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - const handler = ( - context: RequestHandlerContext, - req: KibanaRequest, - res: KibanaResponseFactory - ) => { - const body = { - bar: req.body.bar.toUpperCase(), - baz: req.body.baz.toString(), - }; - - return res.ok({ body }); - }; - - router.post( - { - path: '/', - validate: { - body: ({ bar, baz } = {}, { ok, badRequest }) => { - if (typeof bar === 'string' && typeof baz === 'number') { - return ok({ bar, baz }); - } else { - return badRequest('Wrong payload', ['body']); - } - }, - }, - }, - handler - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .post(`${basePath}/foo/`) - .send({ - bar: 'test', - baz: 123, - }) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ bar: 'TEST', baz: '123' }); - }); - }); - - test('invalid body', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.post( - { - path: '/', - validate: { - body: schema.object({ - bar: schema.number(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: req.body }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .post(`${basePath}/foo/`) - .send({ bar: 'test' }) - .expect(400) - .then((res) => { - expect(res.body).toEqual({ - error: 'Bad Request', - statusCode: 400, - message: '[request body.bar]: expected value of type [number] but got [string]', - }); - }); - }); - - test('handles putting', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.put( - { - path: '/', - validate: { - body: schema.object({ - key: schema.string(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: req.body }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .put(`${basePath}/foo/`) - .send({ key: 'new value' }) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ key: 'new value' }); - }); - }); - - test('handles deleting', async () => { - const router = new Router(`${basePath}/foo`, logger, enhanceWithContext); - - router.delete( - { - path: '/{id}', - validate: { - params: schema.object({ - id: schema.number(), - }), - }, - }, - (_, req, res) => { - return res.ok({ body: { key: req.params.id } }); - } - ); - - const { registerRouter } = await server.setup(config); - registerRouter(router); - - await server.start(); - - await proxySupertest - .delete(`${basePath}/foo/3`) - .expect(200) - .then((res) => { - expect(res.body).toEqual({ key: 3 }); - }); - }); - - describe('with `basepath: /bar` and `rewriteBasePath: false`', () => { - let configWithBasePath: HttpConfig; - - beforeEach(async () => { - configWithBasePath = { - ...config, - basePath: '/bar', - rewriteBasePath: false, - } as HttpConfig; - - const router = new Router(`${basePath}/`, logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (_, __, res) => res.ok({ body: 'value:/' })); - router.get({ path: '/foo', validate: false }, (_, __, res) => res.ok({ body: 'value:/foo' })); - - const { registerRouter } = await server.setup(configWithBasePath); - registerRouter(router); - - await server.start(); - }); - - test('/bar => 404', async () => { - await proxySupertest.get(`${basePath}/bar`).expect(404); - }); - - test('/bar/ => 404', async () => { - await proxySupertest.get(`${basePath}/bar/`).expect(404); - }); - - test('/bar/foo => 404', async () => { - await proxySupertest.get(`${basePath}/bar/foo`).expect(404); - }); - - test('/ => /', async () => { - await proxySupertest - .get(`${basePath}/`) - .expect(200) - .then((res) => { - expect(res.text).toBe('value:/'); - }); - }); - - test('/foo => /foo', async () => { - await proxySupertest - .get(`${basePath}/foo`) - .expect(200) - .then((res) => { - expect(res.text).toBe('value:/foo'); - }); - }); - }); - - test('with defined `redirectHttpFromPort`', async () => { - const router = new Router(`${basePath}/`, logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (_, __, res) => res.ok({ body: 'value:/' })); - - const { registerRouter } = await server.setup(configWithSSL); - registerRouter(router); - - await server.start(); - }); - - test('allows attaching metadata to attach meta-data tag strings to a route', async () => { - const tags = ['my:tag']; - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.get({ path: '/with-tags', validate: false, options: { tags } }, (_, req, res) => - res.ok({ body: { tags: req.route.options.tags } }) - ); - router.get({ path: '/without-tags', validate: false }, (_, req, res) => - res.ok({ body: { tags: req.route.options.tags } }) - ); - registerRouter(router); - - await server.start(); - await proxySupertest.get(`${basePath}/with-tags`).expect(200, { tags }); - - await proxySupertest.get(`${basePath}/without-tags`).expect(200, { tags: [] }); - }); - - describe('response headers', () => { - test('default headers', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.get({ path: '/', validate: false }, (_, req, res) => res.ok({ body: req.route })); - registerRouter(router); - - await server.start(); - const response = await proxySupertest.get(`${basePath}/`).expect(200); - - const restHeaders = omit(response.header, ['date', 'content-length']); - expect(restHeaders).toMatchInlineSnapshot(` - Object { - "accept-ranges": "bytes", - "cache-control": "private, no-cache, no-store, must-revalidate", - "connection": "close", - "content-type": "application/json; charset=utf-8", - } - `); - }); - }); - - test('exposes route details of incoming request to a route handler (POST + payload options)', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.post( - { - path: '/', - validate: { body: schema.object({ test: schema.number() }) }, - options: { body: { accepts: 'application/json' } }, - }, - (_, req, res) => res.ok({ body: req.route }) - ); - registerRouter(router); - - await server.start(); - await proxySupertest - .post(`${basePath}/`) - .send({ test: 1 }) - .expect(200, { - method: 'post', - path: `${basePath}/`, - options: { - authRequired: true, - xsrfRequired: true, - tags: [], - timeout: { - payload: 10000, - idleSocket: 1000, - }, - body: { - parse: true, // hapi populates the default - maxBytes: 1024, // hapi populates the default - accepts: ['application/json'], - output: 'data', - }, - }, - }); - }); - - test('should return a stream in the body', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.put( - { - path: '/', - validate: { body: schema.stream() }, - options: { body: { output: 'stream' } }, - }, - (_, req, res) => { - expect(req.body).toBeInstanceOf(Readable); - return res.ok({ body: req.route.options.body }); - } - ); - registerRouter(router); - - await server.start(); - await proxySupertest.put(`${basePath}/`).send({ test: 1 }).expect(200, { - parse: true, - maxBytes: 1024, // hapi populates the default - output: 'stream', - }); - }); - - describe('timeout options', () => { - describe('payload timeout', () => { - test('POST routes set the payload timeout', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.post( - { - path: '/', - validate: false, - options: { - timeout: { - payload: 300000, - }, - }, - }, - (_, req, res) => { - return res.ok({ - body: { - timeout: req.route.options.timeout, - }, - }); - } - ); - registerRouter(router); - await server.start(); - await proxySupertest - .post(`${basePath}/`) - .send({ test: 1 }) - .expect(200, { - timeout: { - payload: 300000, - idleSocket: 1000, // This is an extra option added by the proxy - }, - }); - }); - - test('DELETE routes set the payload timeout', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.delete( - { - path: '/', - validate: false, - options: { - timeout: { - payload: 300000, - }, - }, - }, - (context, req, res) => { - return res.ok({ - body: { - timeout: req.route.options.timeout, - }, - }); - } - ); - registerRouter(router); - await server.start(); - await proxySupertest.delete(`${basePath}/`).expect(200, { - timeout: { - payload: 300000, - idleSocket: 1000, // This is an extra option added by the proxy - }, - }); - }); - - test('PUT routes set the payload timeout and automatically adjusts the idle socket timeout', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.put( - { - path: '/', - validate: false, - options: { - timeout: { - payload: 300000, - }, - }, - }, - (_, req, res) => { - return res.ok({ - body: { - timeout: req.route.options.timeout, - }, - }); - } - ); - registerRouter(router); - await server.start(); - await proxySupertest.put(`${basePath}/`).expect(200, { - timeout: { - payload: 300000, - idleSocket: 1000, // This is an extra option added by the proxy - }, - }); - }); - - test('PATCH routes set the payload timeout and automatically adjusts the idle socket timeout', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.patch( - { - path: '/', - validate: false, - options: { - timeout: { - payload: 300000, - }, - }, - }, - (_, req, res) => { - return res.ok({ - body: { - timeout: req.route.options.timeout, - }, - }); - } - ); - registerRouter(router); - await server.start(); - await proxySupertest.patch(`${basePath}/`).expect(200, { - timeout: { - payload: 300000, - idleSocket: 1000, // This is an extra option added by the proxy - }, - }); - }); - }); - - describe('idleSocket timeout', () => { - test('uses server socket timeout when not specified in the route', async () => { - const { registerRouter } = await server.setup({ - ...config, - socketTimeout: 11000, - }); - - const router = new Router(basePath, logger, enhanceWithContext); - router.get( - { - path: '/', - validate: { body: schema.maybe(schema.any()) }, - }, - (_, req, res) => { - return res.ok({ - body: { - timeout: req.route.options.timeout, - }, - }); - } - ); - registerRouter(router); - - await server.start(); - await proxySupertest - .get(`${basePath}/`) - .send() - .expect(200, { - timeout: { - idleSocket: 11000, - }, - }); - }); - - test('sets the socket timeout when specified in the route', async () => { - const { registerRouter } = await server.setup({ - ...config, - socketTimeout: 11000, - }); - - const router = new Router(basePath, logger, enhanceWithContext); - router.get( - { - path: '/', - validate: { body: schema.maybe(schema.any()) }, - options: { timeout: { idleSocket: 12000 } }, - }, - (context, req, res) => { - return res.ok({ - body: { - timeout: req.route.options.timeout, - }, - }); - } - ); - registerRouter(router); - - await server.start(); - await proxySupertest - .get(`${basePath}/`) - .send() - .expect(200, { - timeout: { - idleSocket: 12000, - }, - }); - }); - - test('idleSocket timeout can be smaller than the payload timeout', async () => { - const { registerRouter } = await server.setup(config); - - const router = new Router(basePath, logger, enhanceWithContext); - router.post( - { - path: `${basePath}/`, - validate: { body: schema.any() }, - options: { - timeout: { - payload: 1000, - idleSocket: 10, - }, - }, - }, - (_, req, res) => { - return res.ok({ body: { timeout: req.route.options.timeout } }); - } - ); - - registerRouter(router); - - await server.start(); - }); - }); - }); - - describe('shouldRedirect', () => { - let proxyServerWithoutShouldRedirect: BasePathProxyServer; - let proxyWithoutShouldRedirectSupertest: supertest.SuperTest; - - beforeEach(async () => { - // setup and start a proxy server which does not use "shouldRedirectFromOldBasePath" - const proxyConfig: HttpConfig = { ...config, port: 10004 }; - const devConfig = new DevConfig({ basePathProxyTarget: config.port }); - proxyServerWithoutShouldRedirect = new BasePathProxyServer(logger, proxyConfig, devConfig); - const options: Readonly = { - shouldRedirectFromOldBasePath: () => false, // Return false to not redirect - delayUntil: () => EMPTY, - }; - await proxyServerWithoutShouldRedirect.start(options); - proxyWithoutShouldRedirectSupertest = supertest(`http://127.0.0.1:${proxyConfig.port}`); - }); - - afterEach(async () => { - await proxyServerWithoutShouldRedirect.stop(); - }); - - test('it will do a redirect if it detects what looks like a stale or previously used base path', async () => { - const fakeBasePath = basePath !== 'abc' ? 'abc' : 'efg'; - const res = await proxySupertest.get(`/${fakeBasePath}`).expect(302); - const location = res.header.location; - expect(location).toEqual(`${basePath}/`); - }); - - test('it will NOT do a redirect if it detects what looks like a stale or previously used base path if we intentionally turn it off', async () => { - const fakeBasePath = basePath !== 'abc' ? 'abc' : 'efg'; - await proxyWithoutShouldRedirectSupertest.get(`/${fakeBasePath}`).expect(404); - }); - - test('it will NOT redirect if it detects a larger path than 3 characters', async () => { - await proxySupertest.get('/abcde').expect(404); - }); - - test('it will NOT redirect if it is not a GET verb', async () => { - const fakeBasePath = basePath !== 'abc' ? 'abc' : 'efg'; - await proxySupertest.put(`/${fakeBasePath}`).expect(404); - }); - }); - - describe('constructor option for sending in a custom basePath', () => { - let proxyServerWithFooBasePath: BasePathProxyServer; - let proxyWithFooBasePath: supertest.SuperTest; - - beforeEach(async () => { - // setup and start a proxy server which uses a basePath of "foo" - const proxyConfig: HttpConfig = { ...config, port: 10004, basePath: '/foo' }; // <-- "foo" here in basePath - const devConfig = new DevConfig({ basePathProxyTarget: config.port }); - proxyServerWithFooBasePath = new BasePathProxyServer(logger, proxyConfig, devConfig); - const options: Readonly = { - shouldRedirectFromOldBasePath: () => true, - delayUntil: () => EMPTY, - }; - await proxyServerWithFooBasePath.start(options); - proxyWithFooBasePath = supertest(`http://127.0.0.1:${proxyConfig.port}`); - }); - - afterEach(async () => { - await proxyServerWithFooBasePath.stop(); - }); - - test('it will do a redirect to foo which is our passed in value for the configuration', async () => { - const res = await proxyWithFooBasePath.get('/bar').expect(302); - const location = res.header.location; - expect(location).toEqual('/foo/'); - }); - }); -}); diff --git a/src/core/server/http/http_config.ts b/src/core/server/http/http_config.ts index 2bbe9f3f96a55..356dad201ce95 100644 --- a/src/core/server/http/http_config.ts +++ b/src/core/server/http/http_config.ts @@ -7,12 +7,12 @@ */ import { ByteSizeValue, schema, TypeOf } from '@kbn/config-schema'; +import { IHttpConfig, SslConfig, sslSchema } from '@kbn/server-http-tools'; import { hostname } from 'os'; import url from 'url'; import { CspConfigType, CspConfig, ICspConfig } from '../csp'; import { ExternalUrlConfig, IExternalUrlConfig } from '../external_url'; -import { SslConfig, sslSchema } from './ssl_config'; const validBasePathRegex = /^\/.*[^\/]$/; const uuidRegexp = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i; @@ -156,7 +156,7 @@ export const config = { }; export type HttpConfigType = TypeOf; -export class HttpConfig { +export class HttpConfig implements IHttpConfig { public name: string; public autoListen: boolean; public host: string; diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts index 54be7b35f68ad..ccd14d4b99e11 100644 --- a/src/core/server/http/http_server.test.ts +++ b/src/core/server/http/http_server.test.ts @@ -1288,6 +1288,30 @@ test('should return a stream in the body', async () => { }); }); +test('closes sockets on timeout', async () => { + const { registerRouter, server: innerServer } = await server.setup({ + ...config, + socketTimeout: 1000, + }); + const router = new Router('', logger, enhanceWithContext); + + router.get({ path: '/a', validate: false }, async (context, req, res) => { + await new Promise((resolve) => setTimeout(resolve, 2000)); + return res.ok({}); + }); + router.get({ path: '/b', validate: false }, (context, req, res) => res.ok({})); + + registerRouter(router); + + registerRouter(router); + + await server.start(); + + expect(supertest(innerServer.listener).get('/a')).rejects.toThrow('socket hang up'); + + await supertest(innerServer.listener).get('/b').expect(200); +}); + describe('setup contract', () => { describe('#createSessionStorage', () => { test('creates session storage factory', async () => { diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index b0510bc414bf8..cd7d7ccc5aeff 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -10,10 +10,15 @@ import { Server, Request } from '@hapi/hapi'; import HapiStaticFiles from '@hapi/inert'; import url from 'url'; import uuid from 'uuid'; +import { + createServer, + getListenerOptions, + getServerOptions, + getRequestId, +} from '@kbn/server-http-tools'; import { Logger, LoggerFactory } from '../logging'; import { HttpConfig } from './http_config'; -import { createServer, getListenerOptions, getServerOptions, getRequestId } from './http_tools'; import { adoptToHapiAuthFormat, AuthenticationHandler } from './lifecycle/auth'; import { adoptToHapiOnPreAuth, OnPreAuthHandler } from './lifecycle/on_pre_auth'; import { adoptToHapiOnPostAuthFormat, OnPostAuthHandler } from './lifecycle/on_post_auth'; diff --git a/src/core/server/http/http_service.test.ts b/src/core/server/http/http_service.test.ts index 9354c89b63292..83279e99bc476 100644 --- a/src/core/server/http/http_service.test.ts +++ b/src/core/server/http/http_service.test.ts @@ -242,29 +242,6 @@ test('returns http server contract on setup', async () => { }); }); -test('does not start http server if process is dev cluster master', async () => { - const configService = createConfigService(); - const httpServer = { - isListening: () => false, - setup: jest.fn().mockReturnValue({}), - start: jest.fn(), - stop: noop, - }; - mockHttpServer.mockImplementation(() => httpServer); - - const service = new HttpService({ - coreId, - configService, - env: Env.createDefault(REPO_ROOT, getEnvOptions({ isDevCliParent: true })), - logger, - }); - - await service.setup(setupDeps); - await service.start(); - - expect(httpServer.start).not.toHaveBeenCalled(); -}); - test('does not start http server if configured with `autoListen:false`', async () => { const configService = createConfigService({ autoListen: false, diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index 87143e1160c6c..5b90440f6ad70 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -153,15 +153,13 @@ export class HttpService } /** - * Indicates if http server has configured to start listening on a configured port. - * We shouldn't start http service in two cases: - * 1. If `server.autoListen` is explicitly set to `false`. - * 2. When the process is run as dev cluster master in which case cluster manager - * will fork a dedicated process where http service will be set up instead. + * Indicates if http server is configured to start listening on a configured port. + * (if `server.autoListen` is not explicitly set to `false`.) + * * @internal * */ private shouldListen(config: HttpConfig) { - return !this.coreContext.env.isDevCliParent && config.autoListen; + return config.autoListen; } public async stop() { diff --git a/src/core/server/http/http_tools.test.ts b/src/core/server/http/http_tools.test.ts deleted file mode 100644 index c2fa3816324fc..0000000000000 --- a/src/core/server/http/http_tools.test.ts +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -jest.mock('fs', () => { - const original = jest.requireActual('fs'); - return { - // Hapi Inert patches native methods - ...original, - readFileSync: jest.fn(), - }; -}); - -jest.mock('uuid', () => ({ - v4: jest.fn().mockReturnValue('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'), -})); - -import supertest from 'supertest'; -import { Request, ResponseToolkit } from '@hapi/hapi'; -import Joi from 'joi'; - -import { - defaultValidationErrorHandler, - HapiValidationError, - getServerOptions, - getRequestId, -} from './http_tools'; -import { HttpServer } from './http_server'; -import { HttpConfig, config } from './http_config'; -import { Router } from './router'; -import { loggingSystemMock } from '../logging/logging_system.mock'; -import { ByteSizeValue } from '@kbn/config-schema'; - -const emptyOutput = { - statusCode: 400, - headers: {}, - payload: { - statusCode: 400, - error: '', - validation: { - source: '', - keys: [], - }, - }, -}; - -afterEach(() => jest.clearAllMocks()); - -describe('defaultValidationErrorHandler', () => { - it('formats value validation errors correctly', () => { - expect.assertions(1); - const schema = Joi.array().items( - Joi.object({ - type: Joi.string().required(), - }).required() - ); - - const error = schema.validate([{}], { abortEarly: false }).error as HapiValidationError; - - // Emulate what Hapi v17 does by default - error.output = { ...emptyOutput }; - error.output.payload.validation.keys = ['0.type', '']; - - try { - defaultValidationErrorHandler({} as Request, {} as ResponseToolkit, error); - } catch (err) { - // Verify the empty string gets corrected to 'value' - expect(err.output.payload.validation.keys).toEqual(['0.type', 'value']); - } - }); -}); - -describe('timeouts', () => { - const logger = loggingSystemMock.create(); - const server = new HttpServer(logger, 'foo'); - const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {}); - - test('closes sockets on timeout', async () => { - const router = new Router('', logger.get(), enhanceWithContext); - router.get({ path: '/a', validate: false }, async (context, req, res) => { - await new Promise((resolve) => setTimeout(resolve, 2000)); - return res.ok({}); - }); - router.get({ path: '/b', validate: false }, (context, req, res) => res.ok({})); - const { registerRouter, server: innerServer } = await server.setup({ - socketTimeout: 1000, - host: '127.0.0.1', - maxPayload: new ByteSizeValue(1024), - ssl: {}, - cors: { - enabled: false, - }, - compression: { enabled: true }, - requestId: { - allowFromAnyIp: true, - ipAllowlist: [], - }, - } as any); - registerRouter(router); - - await server.start(); - - expect(supertest(innerServer.listener).get('/a')).rejects.toThrow('socket hang up'); - - await supertest(innerServer.listener).get('/b').expect(200); - }); - - afterAll(async () => { - await server.stop(); - }); -}); - -describe('getServerOptions', () => { - beforeEach(() => - jest.requireMock('fs').readFileSync.mockImplementation((path: string) => `content-${path}`) - ); - - it('properly configures TLS with default options', () => { - const httpConfig = new HttpConfig( - config.schema.validate({ - ssl: { - enabled: true, - key: 'some-key-path', - certificate: 'some-certificate-path', - }, - }), - {} as any, - {} as any - ); - - expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(` - Object { - "ca": undefined, - "cert": "content-some-certificate-path", - "ciphers": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", - "honorCipherOrder": true, - "key": "content-some-key-path", - "passphrase": undefined, - "rejectUnauthorized": false, - "requestCert": false, - "secureOptions": 67108864, - } - `); - }); - - it('properly configures TLS with client authentication', () => { - const httpConfig = new HttpConfig( - config.schema.validate({ - ssl: { - enabled: true, - key: 'some-key-path', - certificate: 'some-certificate-path', - certificateAuthorities: ['ca-1', 'ca-2'], - clientAuthentication: 'required', - }, - }), - {} as any, - {} as any - ); - - expect(getServerOptions(httpConfig).tls).toMatchInlineSnapshot(` - Object { - "ca": Array [ - "content-ca-1", - "content-ca-2", - ], - "cert": "content-some-certificate-path", - "ciphers": "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", - "honorCipherOrder": true, - "key": "content-some-key-path", - "passphrase": undefined, - "rejectUnauthorized": true, - "requestCert": true, - "secureOptions": 67108864, - } - `); - }); - - it('properly configures CORS when cors enabled', () => { - const httpConfig = new HttpConfig( - config.schema.validate({ - cors: { - enabled: true, - allowCredentials: false, - allowOrigin: ['*'], - }, - }), - {} as any, - {} as any - ); - - expect(getServerOptions(httpConfig).routes?.cors).toEqual({ - credentials: false, - origin: ['*'], - headers: ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf'], - }); - }); -}); - -describe('getRequestId', () => { - describe('when allowFromAnyIp is true', () => { - it('generates a UUID if no x-opaque-id header is present', () => { - const request = { - headers: {}, - raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, - } as any; - expect(getRequestId(request, { allowFromAnyIp: true, ipAllowlist: [] })).toEqual( - 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - ); - }); - - it('uses x-opaque-id header value if present', () => { - const request = { - headers: { - 'x-opaque-id': 'id from header', - raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, - }, - } as any; - expect(getRequestId(request, { allowFromAnyIp: true, ipAllowlist: [] })).toEqual( - 'id from header' - ); - }); - }); - - describe('when allowFromAnyIp is false', () => { - describe('and ipAllowlist is empty', () => { - it('generates a UUID even if x-opaque-id header is present', () => { - const request = { - headers: { 'x-opaque-id': 'id from header' }, - raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, - } as any; - expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: [] })).toEqual( - 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - ); - }); - }); - - describe('and ipAllowlist is not empty', () => { - it('uses x-opaque-id header if request comes from trusted IP address', () => { - const request = { - headers: { 'x-opaque-id': 'id from header' }, - raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, - } as any; - expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: ['1.1.1.1'] })).toEqual( - 'id from header' - ); - }); - - it('generates a UUID if request comes from untrusted IP address', () => { - const request = { - headers: { 'x-opaque-id': 'id from header' }, - raw: { req: { socket: { remoteAddress: '5.5.5.5' } } }, - } as any; - expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: ['1.1.1.1'] })).toEqual( - 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - ); - }); - - it('generates UUID if request comes from trusted IP address but no x-opaque-id header is present', () => { - const request = { - headers: {}, - raw: { req: { socket: { remoteAddress: '1.1.1.1' } } }, - } as any; - expect(getRequestId(request, { allowFromAnyIp: false, ipAllowlist: ['1.1.1.1'] })).toEqual( - 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' - ); - }); - }); - }); -}); diff --git a/src/core/server/http/http_tools.ts b/src/core/server/http/http_tools.ts deleted file mode 100644 index e909b454feae2..0000000000000 --- a/src/core/server/http/http_tools.ts +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { Server } from '@hapi/hapi'; -import type { - Lifecycle, - Request, - ResponseToolkit, - RouteOptionsCors, - ServerOptions, - Util, -} from '@hapi/hapi'; -import Hoek from '@hapi/hoek'; -import type { ServerOptions as TLSOptions } from 'https'; -import type { ValidationError } from 'joi'; -import uuid from 'uuid'; -import { ensureNoUnsafeProperties } from '@kbn/std'; -import { HttpConfig } from './http_config'; - -const corsAllowedHeaders = ['Accept', 'Authorization', 'Content-Type', 'If-None-Match', 'kbn-xsrf']; -/** - * Converts Kibana `HttpConfig` into `ServerOptions` that are accepted by the Hapi server. - */ -export function getServerOptions(config: HttpConfig, { configureTLS = true } = {}) { - const cors: RouteOptionsCors | false = config.cors.enabled - ? { - credentials: config.cors.allowCredentials, - origin: config.cors.allowOrigin, - headers: corsAllowedHeaders, - } - : false; - // Note that all connection options configured here should be exactly the same - // as in the legacy platform server (see `src/legacy/server/http/index`). Any change - // SHOULD BE applied in both places. The only exception is TLS-specific options, - // that are configured only here. - const options: ServerOptions = { - host: config.host, - port: config.port, - routes: { - cache: { - privacy: 'private', - otherwise: 'private, no-cache, no-store, must-revalidate', - }, - cors, - payload: { - maxBytes: config.maxPayload.getValueInBytes(), - }, - validate: { - failAction: defaultValidationErrorHandler, - options: { - abortEarly: false, - }, - // TODO: This payload validation can be removed once the legacy platform is completely removed. - // This is a default payload validation which applies to all LP routes which do not specify their own - // `validate.payload` handler, in order to reduce the likelyhood of prototype pollution vulnerabilities. - // (All NP routes are already required to specify their own validation in order to access the payload) - payload: (value) => Promise.resolve(ensureNoUnsafeProperties(value)), - }, - }, - state: { - strictHeader: false, - isHttpOnly: true, - isSameSite: false, // necessary to allow using Kibana inside an iframe - }, - }; - - if (configureTLS && config.ssl.enabled) { - const ssl = config.ssl; - - // TODO: Hapi types have a typo in `tls` property type definition: `https.RequestOptions` is used instead of - // `https.ServerOptions`, and `honorCipherOrder` isn't presented in `https.RequestOptions`. - const tlsOptions: TLSOptions = { - ca: ssl.certificateAuthorities, - cert: ssl.certificate, - ciphers: config.ssl.cipherSuites.join(':'), - // We use the server's cipher order rather than the client's to prevent the BEAST attack. - honorCipherOrder: true, - key: ssl.key, - passphrase: ssl.keyPassphrase, - secureOptions: ssl.getSecureOptions(), - requestCert: ssl.requestCert, - rejectUnauthorized: ssl.rejectUnauthorized, - }; - - options.tls = tlsOptions; - } - - return options; -} - -export function getListenerOptions(config: HttpConfig) { - return { - keepaliveTimeout: config.keepaliveTimeout, - socketTimeout: config.socketTimeout, - }; -} - -interface ListenerOptions { - keepaliveTimeout: number; - socketTimeout: number; -} - -export function createServer(serverOptions: ServerOptions, listenerOptions: ListenerOptions) { - const server = new Server(serverOptions); - - server.listener.keepAliveTimeout = listenerOptions.keepaliveTimeout; - server.listener.setTimeout(listenerOptions.socketTimeout); - server.listener.on('timeout', (socket) => { - socket.destroy(); - }); - server.listener.on('clientError', (err, socket) => { - if (socket.writable) { - socket.end(Buffer.from('HTTP/1.1 400 Bad Request\r\n\r\n', 'ascii')); - } else { - socket.destroy(err); - } - }); - - return server; -} - -/** - * Hapi extends the ValidationError interface to add this output key with more data. - */ -export interface HapiValidationError extends ValidationError { - output: { - statusCode: number; - headers: Util.Dictionary; - payload: { - statusCode: number; - error: string; - message?: string; - validation: { - source: string; - keys: string[]; - }; - }; - }; -} - -/** - * Used to replicate Hapi v16 and below's validation responses. Should be used in the routes.validate.failAction key. - */ -export function defaultValidationErrorHandler( - request: Request, - h: ResponseToolkit, - err?: Error -): Lifecycle.ReturnValue { - // Newer versions of Joi don't format the key for missing params the same way. This shim - // provides backwards compatibility. Unfortunately, Joi doesn't export it's own Error class - // in JS so we have to rely on the `name` key before we can cast it. - // - // The Hapi code we're 'overwriting' can be found here: - // https://github.com/hapijs/hapi/blob/master/lib/validation.js#L102 - if (err && err.name === 'ValidationError' && err.hasOwnProperty('output')) { - const validationError: HapiValidationError = err as HapiValidationError; - const validationKeys: string[] = []; - - validationError.details.forEach((detail) => { - if (detail.path.length > 0) { - validationKeys.push(Hoek.escapeHtml(detail.path.join('.'))); - } else { - // If no path, use the value sigil to signal the entire value had an issue. - validationKeys.push('value'); - } - }); - - validationError.output.payload.validation.keys = validationKeys; - } - - throw err; -} - -export function getRequestId(request: Request, options: HttpConfig['requestId']): string { - return options.allowFromAnyIp || - // socket may be undefined in integration tests that connect via the http listener directly - (request.raw.req.socket?.remoteAddress && - options.ipAllowlist.includes(request.raw.req.socket.remoteAddress)) - ? request.headers['x-opaque-id'] ?? uuid.v4() - : uuid.v4(); -} diff --git a/src/core/server/http/https_redirect_server.ts b/src/core/server/http/https_redirect_server.ts index dd29a46d728e7..28909c0308c22 100644 --- a/src/core/server/http/https_redirect_server.ts +++ b/src/core/server/http/https_redirect_server.ts @@ -8,10 +8,10 @@ import { Request, ResponseToolkit, Server } from '@hapi/hapi'; import { format as formatUrl } from 'url'; +import { createServer, getListenerOptions, getServerOptions } from '@kbn/server-http-tools'; import { Logger } from '../logging'; import { HttpConfig } from './http_config'; -import { createServer, getListenerOptions, getServerOptions } from './http_tools'; export class HttpsRedirectServer { private server?: Server; diff --git a/src/core/server/http/index.ts b/src/core/server/http/index.ts index c35b7e2fcd042..84fe5149c89c6 100644 --- a/src/core/server/http/index.ts +++ b/src/core/server/http/index.ts @@ -56,7 +56,6 @@ export type { DestructiveRouteMethod, SafeRouteMethod, } from './router'; -export { BasePathProxyServer } from './base_path_proxy_server'; export type { OnPreRoutingHandler, OnPreRoutingToolkit } from './lifecycle/on_pre_routing'; export type { AuthenticationHandler, diff --git a/src/core/server/http/integration_tests/core_services.test.ts b/src/core/server/http/integration_tests/core_services.test.ts index 6c11534df0d11..af358caae8bfc 100644 --- a/src/core/server/http/integration_tests/core_services.test.ts +++ b/src/core/server/http/integration_tests/core_services.test.ts @@ -540,5 +540,50 @@ describe('http service', () => { expect(header['www-authenticate']).toEqual('Basic realm="Authorization Required"'); }); + + it('provides error reason for Elasticsearch Response Errors', async () => { + const { http } = await root.setup(); + const { createRouter } = http; + // eslint-disable-next-line prefer-const + let elasticsearch: InternalElasticsearchServiceStart; + + esClient.ping.mockImplementation(() => + elasticsearchClientMock.createErrorTransportRequestPromise( + new ResponseError({ + statusCode: 404, + body: { + error: { + type: 'error_type', + reason: 'error_reason', + }, + }, + warnings: [], + headers: {}, + meta: {} as any, + }) + ) + ); + + const router = createRouter('/new-platform'); + router.get({ path: '/', validate: false }, async (context, req, res) => { + try { + const result = await elasticsearch.client.asScoped(req).asInternalUser.ping(); + return res.ok({ + body: result, + }); + } catch (e) { + return res.badRequest({ + body: e, + }); + } + }); + + const coreStart = await root.start(); + elasticsearch = coreStart.elasticsearch; + + const { body } = await kbnTestServer.request.get(root, '/new-platform/').expect(400); + + expect(body.message).toEqual('[error_type]: error_reason'); + }); }); }); diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index 03324dc6c722f..5b297ab44f8bb 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -11,14 +11,12 @@ import Boom from '@hapi/boom'; import supertest from 'supertest'; import { schema } from '@kbn/config-schema'; -import { HttpService } from '../http_service'; - import { contextServiceMock } from '../../context/context_service.mock'; import { loggingSystemMock } from '../../logging/logging_system.mock'; import { createHttpServer } from '../test_utils'; +import { HttpService } from '../http_service'; let server: HttpService; - let logger: ReturnType; const contextSetup = contextServiceMock.createSetupContract(); @@ -28,7 +26,6 @@ const setupDeps = { beforeEach(() => { logger = loggingSystemMock.create(); - server = createHttpServer({ logger }); }); diff --git a/src/core/server/http/router/response_adapter.ts b/src/core/server/http/router/response_adapter.ts index 15c29e261c30b..32a66adc697cf 100644 --- a/src/core/server/http/router/response_adapter.ts +++ b/src/core/server/http/router/response_adapter.ts @@ -14,6 +14,8 @@ import typeDetect from 'type-detect'; import Boom from '@hapi/boom'; import * as stream from 'stream'; +import { isResponseError as isElasticsearchResponseError } from '../../elasticsearch/client/errors'; + import { HttpResponsePayload, KibanaResponse, @@ -147,6 +149,11 @@ function getErrorMessage(payload?: ResponseError): string { throw new Error('expected error message to be provided'); } if (typeof payload === 'string') return payload; + // for ES response errors include nested error reason message. it doesn't contain sensitive data. + if (isElasticsearchResponseError(payload)) { + return `[${payload.message}]: ${payload.meta.body?.error?.reason}`; + } + return getErrorMessage(payload.message); } diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index da6b521bfde9a..db36bd73602c4 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -7,16 +7,12 @@ */ jest.mock('../../../legacy/server/kbn_server'); -jest.mock('./cli_dev_mode'); import { BehaviorSubject, throwError } from 'rxjs'; import { REPO_ROOT } from '@kbn/dev-utils'; -// @ts-expect-error js file to remove TS dependency on cli -import { CliDevMode as MockCliDevMode } from './cli_dev_mode'; import KbnServer from '../../../legacy/server/kbn_server'; import { Config, Env, ObjectToConfigAdapter } from '../config'; -import { BasePathProxyServer } from '../http'; import { DiscoveredPlugin } from '../plugins'; import { getEnvOptions, configServiceMock } from '../config/mocks'; @@ -228,7 +224,6 @@ describe('once LegacyService is set up with connection info', () => { ); expect(MockKbnServer).not.toHaveBeenCalled(); - expect(MockCliDevMode).not.toHaveBeenCalled(); }); test('reconfigures logging configuration if new config is received.', async () => { @@ -335,74 +330,6 @@ describe('once LegacyService is set up without connection info', () => { }); }); -describe('once LegacyService is set up in `devClusterMaster` mode', () => { - beforeEach(() => { - configService.atPath.mockImplementation((path) => { - return new BehaviorSubject( - path === 'dev' ? { basePathProxyTargetPort: 100500 } : { basePath: '/abc' } - ); - }); - }); - - test('creates CliDevMode without base path proxy.', async () => { - const devClusterLegacyService = new LegacyService({ - coreId, - env: Env.createDefault( - REPO_ROOT, - getEnvOptions({ - cliArgs: { silent: true, basePath: false }, - isDevCliParent: true, - }) - ), - logger, - configService: configService as any, - }); - - await devClusterLegacyService.setupLegacyConfig(); - await devClusterLegacyService.setup(setupDeps); - await devClusterLegacyService.start(startDeps); - - expect(MockCliDevMode.fromCoreServices).toHaveBeenCalledTimes(1); - expect(MockCliDevMode.fromCoreServices).toHaveBeenCalledWith( - expect.objectContaining({ silent: true, basePath: false }), - expect.objectContaining({ - get: expect.any(Function), - set: expect.any(Function), - }), - undefined - ); - }); - - test('creates CliDevMode with base path proxy.', async () => { - const devClusterLegacyService = new LegacyService({ - coreId, - env: Env.createDefault( - REPO_ROOT, - getEnvOptions({ - cliArgs: { quiet: true, basePath: true }, - isDevCliParent: true, - }) - ), - logger, - configService: configService as any, - }); - - await devClusterLegacyService.setupLegacyConfig(); - await devClusterLegacyService.setup(setupDeps); - await devClusterLegacyService.start(startDeps); - - expect(MockCliDevMode.fromCoreServices).toHaveBeenCalledTimes(1); - expect(MockCliDevMode.fromCoreServices).toHaveBeenCalledWith( - expect.objectContaining({ quiet: true, basePath: true }), - expect.objectContaining({ - get: expect.any(Function), - set: expect.any(Function), - }), - expect.any(BasePathProxyServer) - ); - }); -}); - describe('start', () => { test('Cannot start without setup phase', async () => { const legacyService = new LegacyService({ diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 63b84e2461e71..f7abe942d0009 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { combineLatest, ConnectableObservable, EMPTY, Observable, Subscription } from 'rxjs'; +import { combineLatest, ConnectableObservable, Observable, Subscription } from 'rxjs'; import { first, map, publishReplay, tap } from 'rxjs/operators'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { PathConfigType } from '@kbn/utils'; @@ -18,9 +18,7 @@ import { CoreService } from '../../types'; import { Config } from '../config'; import { CoreContext } from '../core_context'; import { CspConfigType, config as cspConfig } from '../csp'; -import { DevConfig, DevConfigType, config as devConfig } from '../dev'; import { - BasePathProxyServer, HttpConfig, HttpConfigType, config as httpConfig, @@ -64,7 +62,6 @@ export class LegacyService implements CoreService { /** Symbol to represent the legacy platform as a fake "plugin". Used by the ContextService */ public readonly legacyId = Symbol(); private readonly log: Logger; - private readonly devConfig$: Observable; private readonly httpConfig$: Observable; private kbnServer?: LegacyKbnServer; private configSubscription?: Subscription; @@ -77,9 +74,6 @@ export class LegacyService implements CoreService { const { logger, configService } = coreContext; this.log = logger.get('legacy-service'); - this.devConfig$ = configService - .atPath(devConfig.path) - .pipe(map((rawConfig) => new DevConfig(rawConfig))); this.httpConfig$ = combineLatest( configService.atPath(httpConfig.path), configService.atPath(cspConfig.path), @@ -142,17 +136,12 @@ export class LegacyService implements CoreService { this.log.debug('starting legacy service'); - // Receive initial config and create kbnServer/ClusterManager. - if (this.coreContext.env.isDevCliParent) { - await this.setupCliDevMode(this.legacyRawConfig!); - } else { - this.kbnServer = await this.createKbnServer( - this.settings!, - this.legacyRawConfig!, - setupDeps, - startDeps - ); - } + this.kbnServer = await this.createKbnServer( + this.settings!, + this.legacyRawConfig!, + setupDeps, + startDeps + ); } public async stop() { @@ -169,26 +158,6 @@ export class LegacyService implements CoreService { } } - private async setupCliDevMode(config: LegacyConfig) { - const basePathProxy$ = this.coreContext.env.cliArgs.basePath - ? combineLatest([this.devConfig$, this.httpConfig$]).pipe( - first(), - map( - ([dev, http]) => - new BasePathProxyServer(this.coreContext.logger.get('server'), http, dev) - ) - ) - : EMPTY; - - // eslint-disable-next-line @typescript-eslint/no-var-requires - const { CliDevMode } = require('./cli_dev_mode'); - CliDevMode.fromCoreServices( - this.coreContext.env.cliArgs, - config, - await basePathProxy$.toPromise() - ); - } - private async createKbnServer( settings: LegacyVars, config: LegacyConfig, diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 6a49dd963b4e8..2d54648d22950 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -91,7 +91,7 @@ const createPlugin = ( }); }; -async function testSetup(options: { isDevCliParent?: boolean } = {}) { +async function testSetup() { mockPackage.raw = { branch: 'feature-v1', version: 'v1', @@ -103,10 +103,7 @@ async function testSetup(options: { isDevCliParent?: boolean } = {}) { }; coreId = Symbol('core'); - env = Env.createDefault(REPO_ROOT, { - ...getEnvOptions(), - isDevCliParent: options.isDevCliParent ?? false, - }); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); config$ = new BehaviorSubject>({ plugins: { initialize: true } }); const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); @@ -626,30 +623,3 @@ describe('PluginsService', () => { }); }); }); - -describe('PluginService when isDevCliParent is true', () => { - beforeEach(async () => { - await testSetup({ - isDevCliParent: true, - }); - }); - - describe('#discover()', () => { - it('does not try to run discovery', async () => { - await expect(pluginsService.discover({ environment: environmentSetup })).resolves - .toMatchInlineSnapshot(` - Object { - "pluginPaths": Array [], - "pluginTree": undefined, - "uiPlugins": Object { - "browserConfigs": Map {}, - "internal": Map {}, - "public": Map {}, - }, - } - `); - - expect(mockDiscover).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index 92b06d7b6a09b..8b33e2cf4cc6b 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -7,7 +7,7 @@ */ import Path from 'path'; -import { Observable, EMPTY } from 'rxjs'; +import { Observable } from 'rxjs'; import { filter, first, map, mergeMap, tap, toArray } from 'rxjs/operators'; import { pick } from '@kbn/std'; @@ -75,11 +75,9 @@ export class PluginsService implements CoreService; private readonly pluginConfigDescriptors = new Map(); private readonly uiPluginInternalInfo = new Map(); - private readonly discoveryDisabled: boolean; constructor(private readonly coreContext: CoreContext) { this.log = coreContext.logger.get('plugins-service'); - this.discoveryDisabled = coreContext.env.isDevCliParent; this.pluginsSystem = new PluginsSystem(coreContext); this.configService = coreContext.configService; this.config$ = coreContext.configService @@ -90,14 +88,9 @@ export class PluginsService implements CoreService(); - const initialize = config.initialize && !this.coreContext.env.isDevCliParent; - if (initialize) { + if (config.initialize) { contracts = await this.pluginsSystem.setupPlugins(deps); this.registerPluginStaticDirs(deps); } else { @@ -131,7 +123,7 @@ export class PluginsService implements CoreService void ) { this.loggingSystem = new LoggingSystem(); @@ -87,10 +87,7 @@ export class Root { // Stream that maps config updates to logger updates, including update failures. const update$ = configService.getConfig$().pipe( // always read the logging config when the underlying config object is re-read - // except for the CLI process where we only apply the default logging config once - switchMap(() => - this.env.isDevCliParent ? of(undefined) : configService.atPath('logging') - ), + switchMap(() => configService.atPath('logging')), concatMap((config) => this.loggingSystem.upgrade(config)), // This specifically console.logs because we were not able to configure the logger. // eslint-disable-next-line no-console diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index cf1647ef5cec3..551471d3d3ba8 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -297,7 +297,7 @@ export class BasePath { // Warning: (ae-forgotten-export) The symbol "BootstrapArgs" needs to be exported by the entry point index.d.ts // // @internal (undocumented) -export function bootstrap({ configs, cliArgs, applyConfigOverrides, features, }: BootstrapArgs): Promise; +export function bootstrap({ configs, cliArgs, applyConfigOverrides }: BootstrapArgs): Promise; // @public export interface Capabilities { @@ -343,7 +343,7 @@ export const config: { pingTimeout: Type; logQueries: Type; ssl: import("@kbn/config-schema").ObjectType<{ - verificationMode: Type<"none" | "certificate" | "full">; + verificationMode: Type<"certificate" | "none" | "full">; certificateAuthorities: Type; certificate: Type; key: Type; @@ -1265,10 +1265,10 @@ export type KibanaResponseFactory = typeof kibanaResponseFactory; // @public export const kibanaResponseFactory: { - custom: | Buffer | Error | Stream | { + custom: | Error | Buffer | { message: string | Error; attributes?: Record | undefined; - } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; + } | Stream | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; badRequest: (options?: ErrorHttpResponseOptions) => KibanaResponse; unauthorized: (options?: ErrorHttpResponseOptions) => KibanaResponse; forbidden: (options?: ErrorHttpResponseOptions) => KibanaResponse; diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index f2a2b10fdbfde..fcf09b0295bcb 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -205,19 +205,3 @@ test(`doesn't setup core services if legacy config validation fails`, async () = expect(mockLoggingService.setup).not.toHaveBeenCalled(); expect(mockI18nService.setup).not.toHaveBeenCalled(); }); - -test(`doesn't validate config if env.isDevCliParent is true`, async () => { - const devParentEnv = Env.createDefault(REPO_ROOT, { - ...getEnvOptions(), - isDevCliParent: true, - }); - - const server = new Server(rawConfigService, devParentEnv, logger); - await server.setup(); - - expect(mockEnsureValidConfiguration).not.toHaveBeenCalled(); - expect(mockContextService.setup).toHaveBeenCalled(); - expect(mockHttpService.setup).toHaveBeenCalled(); - expect(mockElasticsearchService.setup).toHaveBeenCalled(); - expect(mockSavedObjectsService.setup).toHaveBeenCalled(); -}); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index ef5164a8c48e1..8905bcd28fe17 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -120,13 +120,10 @@ export class Server { }); const legacyConfigSetup = await this.legacy.setupLegacyConfig(); - // rely on dev server to validate config, don't validate in the parent process - if (!this.env.isDevCliParent) { - // Immediately terminate in case of invalid configuration - // This needs to be done after plugin discovery - await this.configService.validate(); - await ensureValidConfiguration(this.configService, legacyConfigSetup); - } + // Immediately terminate in case of invalid configuration + // This needs to be done after plugin discovery + await this.configService.validate(); + await ensureValidConfiguration(this.configService, legacyConfigSetup); const contextServiceSetup = this.context.setup({ // We inject a fake "legacy plugin" with dependencies on every plugin so that legacy plugins: diff --git a/src/core/server/utils/index.ts b/src/core/server/utils/index.ts index e2dc2c7d99a93..b0776c48f3bed 100644 --- a/src/core/server/utils/index.ts +++ b/src/core/server/utils/index.ts @@ -6,6 +6,5 @@ * Side Public License, v 1. */ -export * from './crypto'; export * from './from_root'; export * from './package_json'; diff --git a/src/core/test_helpers/kbn_server.ts b/src/core/test_helpers/kbn_server.ts index 5e274712ad3a7..d702fed73778f 100644 --- a/src/core/test_helpers/kbn_server.ts +++ b/src/core/test_helpers/kbn_server.ts @@ -70,7 +70,6 @@ export function createRootWithSettings( dist: false, ...cliArgs, }, - isDevCliParent: false, }); return new Root( diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index e4085abe14050..746d035e9bfb6 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -2322,8 +2322,6 @@ export interface SearchError { // @public (undocumented) export class SearchInterceptor { constructor(deps: SearchInterceptorDeps); - // @internal - protected abortController: AbortController; // @internal (undocumented) protected application: CoreStart['application']; // (undocumented) @@ -2334,22 +2332,12 @@ export class SearchInterceptor { // Warning: (ae-forgotten-export) The symbol "AbortError" needs to be exported by the entry point index.d.ts // // (undocumented) - protected handleSearchError(e: KibanaServerError | AbortError, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; + protected handleSearchError(e: KibanaServerError | AbortError, options?: ISearchOptions, isTimeout?: boolean): Error; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) protected runSearch(request: IKibanaSearchRequest, options?: ISearchOptions): Promise; search(request: IKibanaSearchRequest, options?: ISearchOptions): Observable; - // @internal (undocumented) - protected setupAbortSignal({ abortSignal, timeout, }: { - abortSignal?: AbortSignal; - timeout?: number; - }): { - timeoutSignal: AbortSignal; - combinedSignal: AbortSignal; - cleanup: () => void; - abort: () => void; - }; // (undocumented) showError(e: Error): void; } diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index f5a2dc0571fdc..3df2313f83798 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -7,7 +7,7 @@ */ import { memoize } from 'lodash'; -import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } from 'rxjs'; +import { BehaviorSubject, throwError, defer, from, Observable } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { PublicMethodsOf } from '@kbn/utility-types'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; @@ -30,11 +30,7 @@ import { getHttpError, } from './errors'; import { toMountPoint } from '../../../kibana_react/public'; -import { - AbortError, - getCombinedAbortSignal, - KibanaServerError, -} from '../../../kibana_utils/public'; +import { AbortError, KibanaServerError } from '../../../kibana_utils/public'; import { ISessionService } from './session'; export interface SearchInterceptorDeps { @@ -48,12 +44,6 @@ export interface SearchInterceptorDeps { } export class SearchInterceptor { - /** - * `abortController` used to signal all searches to abort. - * @internal - */ - protected abortController = new AbortController(); - /** * Observable that emits when the number of pending requests changes. * @internal @@ -98,10 +88,10 @@ export class SearchInterceptor { */ protected handleSearchError( e: KibanaServerError | AbortError, - timeoutSignal: AbortSignal, - options?: ISearchOptions + options?: ISearchOptions, + isTimeout?: boolean ): Error { - if (timeoutSignal.aborted || e.message === 'Request timed out') { + if (isTimeout || e.message === 'Request timed out') { // Handle a client or a server side timeout const err = new SearchTimeoutError(e, this.getTimeoutMode()); @@ -154,60 +144,6 @@ export class SearchInterceptor { ); } - /** - * @internal - */ - protected setupAbortSignal({ - abortSignal, - timeout, - }: { - abortSignal?: AbortSignal; - timeout?: number; - }) { - // Schedule this request to automatically timeout after some interval - const timeoutController = new AbortController(); - const { signal: timeoutSignal } = timeoutController; - const timeout$ = timeout ? timer(timeout) : NEVER; - const subscription = timeout$.subscribe(() => { - this.deps.usageCollector?.trackQueryTimedOut(); - timeoutController.abort(); - }); - - const selfAbortController = new AbortController(); - - // Get a combined `AbortSignal` that will be aborted whenever the first of the following occurs: - // 1. The internal abort controller aborts - // 2. The request times out - // 3. abort() is called on `selfAbortController`. This is used by session service to abort all pending searches that it tracks - // in the current session - // 4. The passed-in signal aborts (e.g. when re-fetching, or whenever the app determines) - const signals = [ - this.abortController.signal, - timeoutSignal, - selfAbortController.signal, - ...(abortSignal ? [abortSignal] : []), - ]; - - const { signal: combinedSignal, cleanup: cleanupCombinedSignal } = getCombinedAbortSignal( - signals - ); - const cleanup = () => { - subscription.unsubscribe(); - combinedSignal.removeEventListener('abort', cleanup); - cleanupCombinedSignal(); - }; - combinedSignal.addEventListener('abort', cleanup); - - return { - timeoutSignal, - combinedSignal, - cleanup, - abort: () => { - selfAbortController.abort(); - }, - }; - } - private showTimeoutErrorToast = (e: SearchTimeoutError, sessionId?: string) => { this.deps.toasts.addDanger({ title: 'Timed out', @@ -245,25 +181,21 @@ export class SearchInterceptor { */ public search( request: IKibanaSearchRequest, - options?: ISearchOptions + options: ISearchOptions = {} ): Observable { // Defer the following logic until `subscribe` is actually called return defer(() => { - if (options?.abortSignal?.aborted) { + if (options.abortSignal?.aborted) { return throwError(new AbortError()); } - const { timeoutSignal, combinedSignal, cleanup } = this.setupAbortSignal({ - abortSignal: options?.abortSignal, - }); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - return from(this.runSearch(request, { ...options, abortSignal: combinedSignal })).pipe( + return from(this.runSearch(request, options)).pipe( catchError((e: Error | AbortError) => { - return throwError(this.handleSearchError(e, timeoutSignal, options)); + return throwError(this.handleSearchError(e, options)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); - cleanup(); }) ); }); diff --git a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx index 138122d80c765..1be42e1cd6b17 100644 --- a/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx +++ b/src/plugins/discover/public/application/components/sidebar/discover_sidebar.tsx @@ -7,7 +7,8 @@ */ import './discover_sidebar.scss'; -import React, { useCallback, useEffect, useState, useMemo } from 'react'; +import { throttle } from 'lodash'; +import React, { useCallback, useEffect, useState, useMemo, useRef } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiAccordion, @@ -18,7 +19,9 @@ import { EuiSpacer, EuiNotificationBadge, EuiPageSideBar, + useResizeObserver, } from '@elastic/eui'; + import { isEqual, sortBy } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverField } from './discover_field'; @@ -32,6 +35,11 @@ import { FieldFilterState, getDefaultFieldFilter, setFieldFilterProp } from './l import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list'; import { DiscoverSidebarResponsiveProps } from './discover_sidebar_responsive'; +/** + * Default number of available fields displayed and added on scroll + */ +const FIELDS_PER_PAGE = 50; + export interface DiscoverSidebarProps extends DiscoverSidebarResponsiveProps { /** * Current state of the field filter, filtering fields by name, type, ... @@ -66,18 +74,25 @@ export function DiscoverSidebar({ unmappedFieldsConfig, }: DiscoverSidebarProps) { const [fields, setFields] = useState(null); + const [scrollContainer, setScrollContainer] = useState(null); + const [fieldsToRender, setFieldsToRender] = useState(FIELDS_PER_PAGE); + const [fieldsPerPage, setFieldsPerPage] = useState(FIELDS_PER_PAGE); + const availableFieldsContainer = useRef(null); useEffect(() => { const newFields = getIndexPatternFieldList(selectedIndexPattern, fieldCounts); setFields(newFields); }, [selectedIndexPattern, fieldCounts, hits]); + const scrollDimensions = useResizeObserver(scrollContainer); + const onChangeFieldSearch = useCallback( (field: string, value: string | boolean | undefined) => { const newState = setFieldFilterProp(fieldFilter, field, value); setFieldFilter(newState); + setFieldsToRender(fieldsPerPage); }, - [fieldFilter, setFieldFilter] + [fieldFilter, setFieldFilter, setFieldsToRender, fieldsPerPage] ); const getDetailsByField = useCallback( @@ -85,7 +100,10 @@ export function DiscoverSidebar({ [hits, columns, selectedIndexPattern] ); - const popularLimit = services.uiSettings.get(FIELDS_LIMIT_SETTING); + const popularLimit = useMemo(() => services.uiSettings.get(FIELDS_LIMIT_SETTING), [ + services.uiSettings, + ]); + const { selected: selectedFields, popular: popularFields, @@ -112,6 +130,50 @@ export function DiscoverSidebar({ ] ); + const paginate = useCallback(() => { + const newFieldsToRender = fieldsToRender + Math.round(fieldsPerPage * 0.5); + setFieldsToRender(Math.max(fieldsPerPage, Math.min(newFieldsToRender, unpopularFields.length))); + }, [setFieldsToRender, fieldsToRender, unpopularFields, fieldsPerPage]); + + useEffect(() => { + if (scrollContainer && unpopularFields.length && availableFieldsContainer.current) { + const { clientHeight, scrollHeight } = scrollContainer; + const isScrollable = scrollHeight > clientHeight; // there is no scrolling currently + const allFieldsRendered = fieldsToRender >= unpopularFields.length; + + if (!isScrollable && !allFieldsRendered) { + // Not all available fields were rendered with the given fieldsPerPage number + // and no scrolling is available due to the a high zoom out factor of the browser + // In this case the fieldsPerPage needs to be adapted + const fieldsRenderedHeight = availableFieldsContainer.current.clientHeight; + const avgHeightPerItem = Math.round(fieldsRenderedHeight / fieldsToRender); + const newFieldsPerPage = Math.round(clientHeight / avgHeightPerItem) + 10; + if (newFieldsPerPage >= FIELDS_PER_PAGE && newFieldsPerPage !== fieldsPerPage) { + setFieldsPerPage(newFieldsPerPage); + setFieldsToRender(newFieldsPerPage); + } + } + } + }, [ + fieldsPerPage, + scrollContainer, + unpopularFields, + fieldsToRender, + setFieldsPerPage, + setFieldsToRender, + scrollDimensions, + ]); + + const lazyScroll = useCallback(() => { + if (scrollContainer) { + const { scrollTop, clientHeight, scrollHeight } = scrollContainer; + const nearBottom = scrollTop + clientHeight > scrollHeight * 0.9; + if (nearBottom && unpopularFields) { + paginate(); + } + } + }, [paginate, scrollContainer, unpopularFields]); + const fieldTypes = useMemo(() => { const result = ['any']; if (Array.isArray(fields)) { @@ -145,12 +207,19 @@ export function DiscoverSidebar({ return map; }, [fields, useNewFieldsApi, selectedFields]); + const getPaginated = useCallback( + (list) => { + return list.slice(0, fieldsToRender); + }, + [fieldsToRender] + ); + + const filterChanged = useMemo(() => isEqual(fieldFilter, getDefaultFieldFilter()), [fieldFilter]); + if (!selectedIndexPattern || !fields) { return null; } - const filterChanged = isEqual(fieldFilter, getDefaultFieldFilter()); - if (useFlyout) { return (
-
+
{ + if (el && !el.dataset.dynamicScroll) { + el.dataset.dynamicScroll = 'true'; + setScrollContainer(el); + } + }} + onScroll={throttle(lazyScroll, 100)} + className="eui-yScroll" + > {fields.length > 0 && ( - <> +
{selectedFields && selectedFields.length > 0 && selectedFields[0].displayName !== '_source' ? ( @@ -241,11 +319,7 @@ export function DiscoverSidebar({ > {selectedFields.map((field: IndexPatternField) => { return ( -
  • +
  • {popularFields.map((field: IndexPatternField) => { return ( -
  • +
  • - {unpopularFields.map((field: IndexPatternField) => { + {getPaginated(unpopularFields).map((field: IndexPatternField) => { return ( -
  • +
  • - +
  • )}
    diff --git a/src/plugins/kibana_utils/common/abort_utils.test.ts b/src/plugins/kibana_utils/common/abort_utils.test.ts index 1f8a1ef3d84c5..0d34a7852fb44 100644 --- a/src/plugins/kibana_utils/common/abort_utils.test.ts +++ b/src/plugins/kibana_utils/common/abort_utils.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { AbortError, abortSignalToPromise, getCombinedAbortSignal } from './abort_utils'; +import { AbortError, abortSignalToPromise } from './abort_utils'; jest.useFakeTimers(); @@ -66,91 +66,4 @@ describe('AbortUtils', () => { }); }); }); - - describe('getCombinedAbortSignal', () => { - test('should return an AbortSignal', () => { - const signal = getCombinedAbortSignal([]).signal; - expect(signal).toBeInstanceOf(AbortSignal); - }); - - test('should not abort if none of the signals abort', async () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - setTimeout(() => controller1.abort(), 2000); - setTimeout(() => controller2.abort(), 1000); - const signal = getCombinedAbortSignal([controller1.signal, controller2.signal]).signal; - expect(signal.aborted).toBe(false); - jest.advanceTimersByTime(500); - await flushPromises(); - expect(signal.aborted).toBe(false); - }); - - test('should abort when the first signal aborts', async () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - setTimeout(() => controller1.abort(), 2000); - setTimeout(() => controller2.abort(), 1000); - const signal = getCombinedAbortSignal([controller1.signal, controller2.signal]).signal; - expect(signal.aborted).toBe(false); - jest.advanceTimersByTime(1000); - await flushPromises(); - expect(signal.aborted).toBe(true); - }); - - test('should be aborted if any of the signals is already aborted', async () => { - const controller1 = new AbortController(); - const controller2 = new AbortController(); - controller1.abort(); - const signal = getCombinedAbortSignal([controller1.signal, controller2.signal]).signal; - expect(signal.aborted).toBe(true); - }); - - describe('cleanup listener', () => { - const createMockController = () => { - const controller = new AbortController(); - const spyAddListener = jest.spyOn(controller.signal, 'addEventListener'); - const spyRemoveListener = jest.spyOn(controller.signal, 'removeEventListener'); - return { - controller, - getTotalListeners: () => - Math.max(spyAddListener.mock.calls.length - spyRemoveListener.mock.calls.length, 0), - }; - }; - - test('cleanup should cleanup inner listeners', () => { - const controller1 = createMockController(); - const controller2 = createMockController(); - - const { cleanup } = getCombinedAbortSignal([ - controller1.controller.signal, - controller2.controller.signal, - ]); - - expect(controller1.getTotalListeners()).toBe(1); - expect(controller2.getTotalListeners()).toBe(1); - - cleanup(); - - expect(controller1.getTotalListeners()).toBe(0); - expect(controller2.getTotalListeners()).toBe(0); - }); - - test('abort should cleanup inner listeners', async () => { - const controller1 = createMockController(); - const controller2 = createMockController(); - - getCombinedAbortSignal([controller1.controller.signal, controller2.controller.signal]); - - expect(controller1.getTotalListeners()).toBe(1); - expect(controller2.getTotalListeners()).toBe(1); - - controller1.controller.abort(); - - await flushPromises(); - - expect(controller1.getTotalListeners()).toBe(0); - expect(controller2.getTotalListeners()).toBe(0); - }); - }); - }); }); diff --git a/src/plugins/kibana_utils/common/abort_utils.ts b/src/plugins/kibana_utils/common/abort_utils.ts index f4c750745a605..051f947b68c1b 100644 --- a/src/plugins/kibana_utils/common/abort_utils.ts +++ b/src/plugins/kibana_utils/common/abort_utils.ts @@ -45,32 +45,3 @@ export function abortSignalToPromise( return { promise, cleanup }; } - -/** - * Returns an `AbortSignal` that will be aborted when the first of the given signals aborts. - * - * @param signals - */ -export function getCombinedAbortSignal( - signals: AbortSignal[] -): { signal: AbortSignal; cleanup: () => void } { - const controller = new AbortController(); - let cleanup = () => {}; - - if (signals.some((signal) => signal.aborted)) { - controller.abort(); - } else { - const promises = signals.map((signal) => abortSignalToPromise(signal)); - cleanup = () => { - promises.forEach((p) => p.cleanup()); - controller.signal.removeEventListener('abort', cleanup); - }; - controller.signal.addEventListener('abort', cleanup); - Promise.race(promises.map((p) => p.promise)).catch(() => { - cleanup(); - controller.abort(); - }); - } - - return { signal: controller.signal, cleanup }; -} diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts index 398bf1415c005..76a7cb2855c6e 100644 --- a/src/plugins/kibana_utils/common/index.ts +++ b/src/plugins/kibana_utils/common/index.ts @@ -13,7 +13,7 @@ export * from './ui'; export * from './state_containers'; export * from './typed_json'; export * from './errors'; -export { AbortError, abortSignalToPromise, getCombinedAbortSignal } from './abort_utils'; +export { AbortError, abortSignalToPromise } from './abort_utils'; export { createGetterSetter, Get, Set } from './create_getter_setter'; export { distinctUntilChangedWithInitialValue } from './distinct_until_changed_with_initial_value'; export { url } from './url'; diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 9a94757cdcb7a..75c52e1301ea5 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -15,7 +15,6 @@ export { fieldWildcardFilter, fieldWildcardMatcher, Get, - getCombinedAbortSignal, JsonArray, JsonObject, JsonValue, diff --git a/src/plugins/kibana_utils/server/index.ts b/src/plugins/kibana_utils/server/index.ts index babc5c4a201ee..483c5aa92b45e 100644 --- a/src/plugins/kibana_utils/server/index.ts +++ b/src/plugins/kibana_utils/server/index.ts @@ -13,7 +13,6 @@ export { fieldWildcardFilter, fieldWildcardMatcher, Get, - getCombinedAbortSignal, Set, url, } from '../common'; diff --git a/test/functional/apps/discover/_huge_fields.ts b/test/functional/apps/discover/_huge_fields.ts new file mode 100644 index 0000000000000..8cb39feb2e6bb --- /dev/null +++ b/test/functional/apps/discover/_huge_fields.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'timePicker']); + const kibanaServer = getService('kibanaServer'); + const testSubjects = getService('testSubjects'); + + describe('test large number of fields in sidebar', function () { + before(async function () { + await security.testUser.setRoles(['kibana_admin', 'test_testhuge_reader'], false); + await esArchiver.loadIfNeeded('large_fields'); + await PageObjects.settings.navigateTo(); + await kibanaServer.uiSettings.update({ + 'timepicker:timeDefaults': `{ "from": "2016-10-05T00:00:00", "to": "2016-10-06T00:00:00"}`, + }); + await PageObjects.settings.createIndexPattern('*huge*', 'date', true); + await PageObjects.common.navigateToApp('discover'); + }); + + it('test_huge data should have expected number of fields', async function () { + await PageObjects.discover.selectIndexPattern('*huge*'); + // initially this field should not be rendered + const fieldExistsBeforeScrolling = await testSubjects.exists('field-myvar1050'); + expect(fieldExistsBeforeScrolling).to.be(false); + // scrolling down a little, should render this field + await testSubjects.scrollIntoView('fieldToggle-myvar1029'); + const fieldExistsAfterScrolling = await testSubjects.exists('field-myvar1050'); + expect(fieldExistsAfterScrolling).to.be(true); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + await esArchiver.unload('large_fields'); + await kibanaServer.uiSettings.replace({}); + }); + }); +} diff --git a/test/functional/apps/discover/index.ts b/test/functional/apps/discover/index.ts index 5c319312c8137..e526cdaccbd4c 100644 --- a/test/functional/apps/discover/index.ts +++ b/test/functional/apps/discover/index.ts @@ -47,5 +47,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_data_grid_doc_navigation')); loadTestFile(require.resolve('./_data_grid_doc_table')); loadTestFile(require.resolve('./_indexpattern_with_unmapped_fields')); + loadTestFile(require.resolve('./_huge_fields')); }); } diff --git a/x-pack/examples/alerting_example/public/components/view_alert.tsx b/x-pack/examples/alerting_example/public/components/view_alert.tsx index 8c942d685af27..40eeb9fd360dc 100644 --- a/x-pack/examples/alerting_example/public/components/view_alert.tsx +++ b/x-pack/examples/alerting_example/public/components/view_alert.tsx @@ -21,8 +21,12 @@ import { import { withRouter, RouteComponentProps } from 'react-router-dom'; import { CoreStart } from 'kibana/public'; import { isEmpty } from 'lodash'; -import { Alert, AlertTaskState, BASE_ALERT_API_PATH } from '../../../../plugins/alerting/common'; import { ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; +import { + Alert, + AlertTaskState, + LEGACY_BASE_ALERT_API_PATH, +} from '../../../../plugins/alerting/common'; type Props = RouteComponentProps & { http: CoreStart['http']; @@ -34,10 +38,10 @@ export const ViewAlertPage = withRouter(({ http, id }: Props) => { useEffect(() => { if (!alert) { - http.get(`${BASE_ALERT_API_PATH}/alert/${id}`).then(setAlert); + http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}`).then(setAlert); } if (!alertState) { - http.get(`${BASE_ALERT_API_PATH}/alert/${id}/state`).then(setAlertState); + http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/state`).then(setAlertState); } }, [alert, alertState, http, id]); diff --git a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx index 7e8487b0179fa..8eef1882b9389 100644 --- a/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx +++ b/x-pack/examples/alerting_example/public/components/view_astros_alert.tsx @@ -23,8 +23,12 @@ import { import { withRouter, RouteComponentProps } from 'react-router-dom'; import { CoreStart } from 'kibana/public'; import { isEmpty } from 'lodash'; -import { Alert, AlertTaskState, BASE_ALERT_API_PATH } from '../../../../plugins/alerting/common'; import { ALERTING_EXAMPLE_APP_ID, AlwaysFiringParams } from '../../common/constants'; +import { + Alert, + AlertTaskState, + LEGACY_BASE_ALERT_API_PATH, +} from '../../../../plugins/alerting/common'; type Props = RouteComponentProps & { http: CoreStart['http']; @@ -40,10 +44,10 @@ export const ViewPeopleInSpaceAlertPage = withRouter(({ http, id }: Props) => { useEffect(() => { if (!alert) { - http.get(`${BASE_ALERT_API_PATH}/alert/${id}`).then(setAlert); + http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}`).then(setAlert); } if (!alertState) { - http.get(`${BASE_ALERT_API_PATH}/alert/${id}/state`).then(setAlertState); + http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/state`).then(setAlertState); } }, [alert, alertState, http, id]); diff --git a/x-pack/examples/embedded_lens_example/public/app.tsx b/x-pack/examples/embedded_lens_example/public/app.tsx index 71af1b824eb80..d2af98e8e85e4 100644 --- a/x-pack/examples/embedded_lens_example/public/app.tsx +++ b/x-pack/examples/embedded_lens_example/public/app.tsx @@ -111,7 +111,13 @@ export const App = (props: { defaultIndexPattern: IndexPattern | null; }) => { const [color, setColor] = useState('green'); + const [isLoading, setIsLoading] = useState(false); const LensComponent = props.plugins.lens.EmbeddableComponent; + + const [time, setTime] = useState({ + from: 'now-5d', + to: 'now', + }); return ( @@ -138,6 +144,7 @@ export const App = (props: { { // eslint-disable-next-line no-bitwise const newColor = '#' + ((Math.random() * 0xffffff) << 0).toString(16); @@ -153,10 +160,7 @@ export const App = (props: { onClick={() => { props.plugins.lens.navigateToPrefilledEditor({ id: '', - timeRange: { - from: 'now-5d', - to: 'now', - }, + timeRange: time, attributes: getLensAttributes(props.defaultIndexPattern!, color), }); // eslint-disable-next-line no-bitwise @@ -171,11 +175,23 @@ export const App = (props: { { + setIsLoading(val); + }} + onBrushEnd={({ range }) => { + setTime({ + from: new Date(range[0]).toISOString(), + to: new Date(range[1]).toISOString(), + }); + }} + onFilter={(_data) => { + // call back event for on filter event + }} + onTableRowClick={(_data) => { + // call back event for on table row click event + }} /> ) : ( diff --git a/x-pack/plugins/alerting/README.md b/x-pack/plugins/alerting/README.md index 19322fed7363e..eb64d71be565e 100644 --- a/x-pack/plugins/alerting/README.md +++ b/x-pack/plugins/alerting/README.md @@ -453,20 +453,20 @@ The only case in which this handler will not be used to evaluate the navigation You can use the `registerNavigation` api to specify as many AlertType specific handlers as you like, but you can only use it once per AlertType as we wouldn't know which handler to use if you specified two for the same AlertType. For the same reason, you can only use `registerDefaultNavigation` once per plugin, as it covers all cases for your specific plugin. -## Experimental RESTful API +## Internal HTTP APIs -Using of the alert type requires you to create an alert that will contain parameters and actions for a given alert type. API description for CRUD operations is a part of the [user documentation](https://www.elastic.co/guide/en/kibana/master/alerts-api-update.html). -API listed below is experimental and could be changed or removed in the future. +Using of the rule type requires you to create a rule that will contain parameters and actions for a given rule type. API description for CRUD operations is a part of the [user documentation](https://www.elastic.co/guide/en/kibana/master/alerting-apis.html). +API listed below are internal and should not be consumed by plugin outside the alerting plugins. -### `GET /api/alerts/alert/{id}/state`: Get alert state +### `GET /internal/alerting/rule/{id}/state`: Get rule state Params: |Property|Description|Type| |---|---|---| -|id|The id of the alert whose state you're trying to get.|string| +|id|The id of the rule whose state you're trying to get.|string| -### `GET /api/alerts/alert/{id}/_instance_summary`: Get alert instance summary +### `GET /internal/alerting/rule/{id}/_alert_summary`: Get rule alert summary Similar to the `GET state` call, but collects additional information from the event log. @@ -475,7 +475,7 @@ Params: |Property|Description|Type| |---|---|---| -|id|The id of the alert whose instance summary you're trying to get.|string| +|id|The id of the rule whose alert summary you're trying to get.|string| Query: @@ -483,11 +483,11 @@ Query: |---|---|---| |dateStart|The date to start looking for alert events in the event log. Either an ISO date string, or a duration string indicating time since now.|string| -### `POST /api/alerts/alert/{id}/_update_api_key`: Update alert API key +### `POST /internal/alerting/rule/{id}/_update_api_key`: Update rule API key |Property|Description|Type| |---|---|---| -|id|The id of the alert you're trying to update the API key for. System will use user in request context to generate an API key for.|string| +|id|The id of the rule you're trying to update the API key for. System will use user in request context to generate an API key for.|string| ## Alert instance factory diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index ff1540090a357..3530abb7384ea 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -24,5 +24,7 @@ export interface AlertingFrameworkHealth { alertingFrameworkHeath: AlertsHealth; } -export const BASE_ALERT_API_PATH = '/api/alerts'; +export const LEGACY_BASE_ALERT_API_PATH = '/api/alerts'; +export const BASE_ALERTING_API_PATH = '/api/alerting'; +export const INTERNAL_BASE_ALERTING_API_PATH = '/internal/alerting'; export const ALERTS_FEATURE_ID = 'alerts'; diff --git a/x-pack/plugins/alerting/public/alert_api.ts b/x-pack/plugins/alerting/public/alert_api.ts index 6eb2e29a7e8e5..d1213c80b95be 100644 --- a/x-pack/plugins/alerting/public/alert_api.ts +++ b/x-pack/plugins/alerting/public/alert_api.ts @@ -7,11 +7,11 @@ import { HttpSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; -import { BASE_ALERT_API_PATH } from '../common'; +import { LEGACY_BASE_ALERT_API_PATH } from '../common'; import type { Alert, AlertType } from '../common'; export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`); + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/list_alert_types`); } export async function loadAlertType({ @@ -22,7 +22,7 @@ export async function loadAlertType({ id: AlertType['id']; }): Promise { const maybeAlertType = ((await http.get( - `${BASE_ALERT_API_PATH}/list_alert_types` + `${LEGACY_BASE_ALERT_API_PATH}/list_alert_types` )) as AlertType[]).find((type) => type.id === id); if (!maybeAlertType) { throw new Error( @@ -44,5 +44,5 @@ export async function loadAlert({ http: HttpSetup; alertId: string; }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/alert/${alertId}`); + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alertId}`); } diff --git a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts index 1b29191d9063e..e316ecd3c6fec 100644 --- a/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client/alerts_client.ts @@ -125,7 +125,7 @@ interface IndexType { [key: string]: unknown; } -interface AggregateResult { +export interface AggregateResult { alertExecutionStatus: { [status: string]: number }; } @@ -157,7 +157,7 @@ export interface CreateOptions { }; } -interface UpdateOptions { +export interface UpdateOptions { id: string; data: { name: string; @@ -170,7 +170,7 @@ interface UpdateOptions { }; } -interface GetAlertInstanceSummaryParams { +export interface GetAlertInstanceSummaryParams { id: string; dateStart?: string; } @@ -229,7 +229,7 @@ export class AlertsClient { public async create({ data, options, - }: CreateOptions): Promise> { + }: CreateOptions): Promise> { const id = options?.id || SavedObjectsUtils.generateId(); try { diff --git a/x-pack/plugins/alerting/server/lib/errors/index.ts b/x-pack/plugins/alerting/server/lib/errors/index.ts new file mode 100644 index 0000000000000..9c6d233f15d3d --- /dev/null +++ b/x-pack/plugins/alerting/server/lib/errors/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { ErrorThatHandlesItsOwnResponse } from './types'; + +export function isErrorThatHandlesItsOwnResponse( + e: ErrorThatHandlesItsOwnResponse +): e is ErrorThatHandlesItsOwnResponse { + return typeof (e as ErrorThatHandlesItsOwnResponse).sendResponse === 'function'; +} + +export { ErrorThatHandlesItsOwnResponse }; +export { AlertTypeDisabledError, AlertTypeDisabledReason } from './alert_type_disabled'; diff --git a/x-pack/plugins/alerting/server/lib/index.ts b/x-pack/plugins/alerting/server/lib/index.ts index 3fd0ac403f8f8..493b004106933 100644 --- a/x-pack/plugins/alerting/server/lib/index.ts +++ b/x-pack/plugins/alerting/server/lib/index.ts @@ -6,10 +6,17 @@ */ export { parseDuration, validateDurationSchema } from '../../common/parse_duration'; -export { LicenseState } from './license_state'; +export { ILicenseState, LicenseState } from './license_state'; export { validateAlertTypeParams } from './validate_alert_type_params'; export { getAlertNotifyWhenType } from './get_alert_notify_when_type'; +export { verifyApiAccess } from './license_api_access'; export { ErrorWithReason, getReasonFromError, isErrorWithReason } from './error_with_reason'; +export { + AlertTypeDisabledError, + AlertTypeDisabledReason, + ErrorThatHandlesItsOwnResponse, + isErrorThatHandlesItsOwnResponse, +} from './errors'; export { executionStatusFromState, executionStatusFromError, diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index ff36ebcd84ba5..787d3cc548ba1 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -37,25 +37,7 @@ import { } from '../../../../src/core/server'; import type { AlertingRequestHandlerContext } from './types'; -import { - aggregateAlertRoute, - createAlertRoute, - deleteAlertRoute, - findAlertRoute, - getAlertRoute, - getAlertStateRoute, - getAlertInstanceSummaryRoute, - listAlertTypesRoute, - updateAlertRoute, - enableAlertRoute, - disableAlertRoute, - updateApiKeyRoute, - muteAllAlertRoute, - unmuteAllAlertRoute, - muteAlertInstanceRoute, - unmuteAlertInstanceRoute, - healthRoute, -} from './routes'; +import { defineRoutes } from './routes'; import { LICENSE_TYPE, LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; import { PluginSetupContract as ActionsPluginSetupContract, @@ -266,23 +248,7 @@ export class AlertingPlugin { // Routes const router = core.http.createRouter(); // Register routes - aggregateAlertRoute(router, this.licenseState); - createAlertRoute(router, this.licenseState); - deleteAlertRoute(router, this.licenseState); - findAlertRoute(router, this.licenseState); - getAlertRoute(router, this.licenseState); - getAlertStateRoute(router, this.licenseState); - getAlertInstanceSummaryRoute(router, this.licenseState); - listAlertTypesRoute(router, this.licenseState); - updateAlertRoute(router, this.licenseState); - enableAlertRoute(router, this.licenseState); - disableAlertRoute(router, this.licenseState); - updateApiKeyRoute(router, this.licenseState); - muteAllAlertRoute(router, this.licenseState); - unmuteAllAlertRoute(router, this.licenseState); - muteAlertInstanceRoute(router, this.licenseState); - unmuteAlertInstanceRoute(router, this.licenseState); - healthRoute(router, this.licenseState, plugins.encryptedSavedObjects); + defineRoutes(router, this.licenseState, plugins.encryptedSavedObjects); return { registerType< diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts new file mode 100644 index 0000000000000..26c06eae33d0a --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.test.ts @@ -0,0 +1,150 @@ +/* + * 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 { aggregateRulesRoute } from './aggregate_rules'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('aggregateRulesRoute', () => { + it('aggregate rules with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + aggregateRulesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rules/_aggregate"`); + + const aggregateResult = { + alertExecutionStatus: { + ok: 15, + error: 2, + active: 23, + pending: 1, + unknown: 0, + }, + }; + alertsClient.aggregate.mockResolvedValueOnce(aggregateResult); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + query: { + default_search_operator: 'AND', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "rule_execution_status": Object { + "active": 23, + "error": 2, + "ok": 15, + "pending": 1, + "unknown": 0, + }, + }, + } + `); + + expect(alertsClient.aggregate).toHaveBeenCalledTimes(1); + expect(alertsClient.aggregate.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "options": Object { + "defaultSearchOperator": "AND", + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: { + rule_execution_status: { + ok: 15, + error: 2, + active: 23, + pending: 1, + unknown: 0, + }, + }, + }); + }); + + it('ensures the license allows aggregating rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + aggregateRulesRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.aggregate.mockResolvedValueOnce({ + alertExecutionStatus: { + ok: 15, + error: 2, + active: 23, + pending: 1, + unknown: 0, + }, + }); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + query: { + default_search_operator: 'OR', + }, + } + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents aggregating rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + aggregateRulesRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + const [context, req, res] = mockHandlerArguments( + {}, + { + query: {}, + }, + ['ok'] + ); + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/aggregate_rules.ts b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts new file mode 100644 index 0000000000000..ecebd7851af6b --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/aggregate_rules.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState } from '../lib'; +import { AggregateResult, AggregateOptions } from '../alerts_client'; +import { RewriteResponseCase, RewriteRequestCase, verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; + +// config definition +const querySchema = schema.object({ + search: schema.maybe(schema.string()), + default_search_operator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], { + defaultValue: 'OR', + }), + search_fields: schema.maybe(schema.arrayOf(schema.string())), + has_reference: schema.maybe( + // use nullable as maybe is currently broken + // in config-schema + schema.nullable( + schema.object({ + type: schema.string(), + id: schema.string(), + }) + ) + ), + filter: schema.maybe(schema.string()), +}); + +const rewriteQueryReq: RewriteRequestCase = ({ + default_search_operator: defaultSearchOperator, + has_reference: hasReference, + search_fields: searchFields, + ...rest +}) => ({ + ...rest, + defaultSearchOperator, + ...(hasReference ? { hasReference } : {}), + ...(searchFields ? { searchFields } : {}), +}); +const rewriteBodyRes: RewriteResponseCase = ({ + alertExecutionStatus, + ...rest +}) => ({ + ...rest, + rule_execution_status: alertExecutionStatus, +}); + +export const aggregateRulesRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/rules/_aggregate`, + validate: { + query: querySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const options = rewriteQueryReq({ + ...req.query, + has_reference: req.query.has_reference || undefined, + }); + const aggregateResult = await alertsClient.aggregate({ options }); + return res.ok({ + body: rewriteBodyRes(aggregateResult), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/create_rule.test.ts b/x-pack/plugins/alerting/server/routes/create_rule.test.ts new file mode 100644 index 0000000000000..5dbc5014ef6ba --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/create_rule.test.ts @@ -0,0 +1,298 @@ +/* + * 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 { pick } from 'lodash'; +import { createRuleRoute } from './create_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { CreateOptions } from '../alerts_client'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib'; +import { AsApiContract } from './lib'; +import { SanitizedAlert } from '../types'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('createRuleRoute', () => { + const createdAt = new Date(); + const updatedAt = new Date(); + + const mockedAlert: SanitizedAlert<{ bar: boolean }> = { + alertTypeId: '1', + consumer: 'bar', + name: 'abc', + schedule: { interval: '10s' }, + tags: ['foo'], + params: { + bar: true, + }, + throttle: '30s', + actions: [ + { + actionTypeId: 'test', + group: 'default', + id: '2', + params: { + foo: true, + }, + }, + ], + enabled: true, + muteAll: false, + createdBy: '', + updatedBy: '', + apiKeyOwner: '', + mutedInstanceIds: [], + notifyWhen: 'onActionGroupChange', + createdAt, + updatedAt, + id: '123', + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + }; + + const ruleToCreate: AsApiContract['data']> = { + ...pick(mockedAlert, 'consumer', 'name', 'schedule', 'tags', 'params', 'throttle', 'enabled'), + rule_type_id: mockedAlert.alertTypeId, + notify_when: mockedAlert.notifyWhen, + actions: [ + { + group: mockedAlert.actions[0].group, + id: mockedAlert.actions[0].id, + params: mockedAlert.actions[0].params, + }, + ], + }; + + const createResult: AsApiContract> = { + ...ruleToCreate, + mute_all: mockedAlert.muteAll, + created_by: mockedAlert.createdBy, + updated_by: mockedAlert.updatedBy, + api_key_owner: mockedAlert.apiKeyOwner, + muted_alert_ids: mockedAlert.mutedInstanceIds, + created_at: mockedAlert.createdAt, + updated_at: mockedAlert.updatedAt, + id: mockedAlert.id, + execution_status: { + status: mockedAlert.executionStatus.status, + last_execution_date: mockedAlert.executionStatus.lastExecutionDate, + }, + actions: [ + { + ...ruleToCreate.actions[0], + connector_type_id: 'test', + }, + ], + }; + + it('creates a rule with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + createRuleRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id?}"`); + + alertsClient.create.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + body: ruleToCreate, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toEqual({ body: createResult }); + + expect(alertsClient.create).toHaveBeenCalledTimes(1); + expect(alertsClient.create.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "data": Object { + "actions": Array [ + Object { + "group": "default", + "id": "2", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "1", + "consumer": "bar", + "enabled": true, + "name": "abc", + "notifyWhen": "onActionGroupChange", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "tags": Array [ + "foo", + ], + "throttle": "30s", + }, + "options": Object { + "id": undefined, + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: createResult, + }); + }); + + it('allows providing a custom id', async () => { + const expectedResult = { + ...createResult, + id: 'custom-id', + }; + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + createRuleRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id?}"`); + + alertsClient.create.mockResolvedValueOnce({ + ...mockedAlert, + id: 'custom-id', + }); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: 'custom-id' }, + body: ruleToCreate, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toEqual({ body: expectedResult }); + + expect(alertsClient.create).toHaveBeenCalledTimes(1); + expect(alertsClient.create.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "data": Object { + "actions": Array [ + Object { + "group": "default", + "id": "2", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "1", + "consumer": "bar", + "enabled": true, + "name": "abc", + "notifyWhen": "onActionGroupChange", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "tags": Array [ + "foo", + ], + "throttle": "30s", + }, + "options": Object { + "id": "custom-id", + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: expectedResult, + }); + }); + + it('ensures the license allows creating rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + createRuleRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.create.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { body: ruleToCreate }); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents creating rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + createRuleRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.create.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, {}); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + createRuleRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.create.mockRejectedValue(new AlertTypeDisabledError('Fail', 'license_invalid')); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { body: ruleToCreate }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/create_rule.ts b/x-pack/plugins/alerting/server/routes/create_rule.ts new file mode 100644 index 0000000000000..4e31db970ccc6 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/create_rule.ts @@ -0,0 +1,141 @@ +/* + * 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 { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { validateDurationSchema, ILicenseState, AlertTypeDisabledError } from '../lib'; +import { CreateOptions } from '../alerts_client'; +import { + RewriteRequestCase, + RewriteResponseCase, + handleDisabledApiKeysError, + verifyAccessAndContext, +} from './lib'; +import { + SanitizedAlert, + validateNotifyWhenType, + AlertTypeParams, + AlertingRequestHandlerContext, + BASE_ALERTING_API_PATH, + AlertNotifyWhenType, +} from '../types'; + +export const bodySchema = schema.object({ + name: schema.string(), + rule_type_id: schema.string(), + enabled: schema.boolean({ defaultValue: true }), + consumer: schema.string(), + tags: schema.arrayOf(schema.string(), { defaultValue: [] }), + throttle: schema.nullable(schema.string({ validate: validateDurationSchema })), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + schedule: schema.object({ + interval: schema.string({ validate: validateDurationSchema }), + }), + actions: schema.arrayOf( + schema.object({ + group: schema.string(), + id: schema.string(), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + }), + { defaultValue: [] } + ), + notify_when: schema.string({ validate: validateNotifyWhenType }), +}); + +const rewriteBodyReq: RewriteRequestCase['data']> = ({ + rule_type_id: alertTypeId, + notify_when: notifyWhen, + ...rest +}) => ({ + ...rest, + alertTypeId, + notifyWhen, +}); +const rewriteBodyRes: RewriteResponseCase> = ({ + actions, + alertTypeId, + scheduledTaskId, + createdBy, + updatedBy, + createdAt, + updatedAt, + apiKeyOwner, + notifyWhen, + muteAll, + mutedInstanceIds, + executionStatus: { lastExecutionDate, ...executionStatus }, + ...rest +}) => ({ + ...rest, + rule_type_id: alertTypeId, + scheduled_task_id: scheduledTaskId, + created_by: createdBy, + updated_by: updatedBy, + created_at: createdAt, + updated_at: updatedAt, + api_key_owner: apiKeyOwner, + notify_when: notifyWhen, + mute_all: muteAll, + muted_alert_ids: mutedInstanceIds, + execution_status: { + ...executionStatus, + last_execution_date: lastExecutionDate, + }, + actions: actions.map(({ group, id, actionTypeId, params }) => ({ + group, + id, + params, + connector_type_id: actionTypeId, + })), +}); + +export const createRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id?}`, + validate: { + params: schema.maybe( + schema.object({ + id: schema.maybe(schema.string()), + }) + ), + body: bodySchema, + }, + }, + handleDisabledApiKeysError( + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const rule = req.body; + const params = req.params; + try { + const createdRule: SanitizedAlert = await alertsClient.create( + { + data: rewriteBodyReq({ + ...rule, + notify_when: rule.notify_when as AlertNotifyWhenType, + }), + options: { id: params?.id }, + } + ); + return res.ok({ + body: rewriteBodyRes(createdRule), + }); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/delete_rule.test.ts b/x-pack/plugins/alerting/server/routes/delete_rule.test.ts new file mode 100644 index 0000000000000..16d344548fc25 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/delete_rule.test.ts @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { deleteRuleRoute } from './delete_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('deleteRuleRoute', () => { + it('deletes an alert with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + deleteRuleRoute(router, licenseState); + + const [config, handler] = router.delete.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}"`); + + alertsClient.delete.mockResolvedValueOnce({}); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.delete).toHaveBeenCalledTimes(1); + expect(alertsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the license allows deleting rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + deleteRuleRoute(router, licenseState); + + const [, handler] = router.delete.mock.calls[0]; + + alertsClient.delete.mockResolvedValueOnce({}); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: '1' }, + } + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents deleting rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + deleteRuleRoute(router, licenseState); + + const [, handler] = router.delete.mock.calls[0]; + + alertsClient.delete.mockResolvedValueOnce({}); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + id: '1', + } + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/delete_rule.ts b/x-pack/plugins/alerting/server/routes/delete_rule.ts new file mode 100644 index 0000000000000..724eb5352ed23 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/delete_rule.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState } from '../lib'; +import { verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const deleteRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.delete( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id}`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + await alertsClient.delete({ id }); + return res.noContent(); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/disable_rule.test.ts b/x-pack/plugins/alerting/server/routes/disable_rule.test.ts new file mode 100644 index 0000000000000..a77a8443a97fb --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/disable_rule.test.ts @@ -0,0 +1,81 @@ +/* + * 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 { disableRuleRoute } from './disable_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('disableRuleRoute', () => { + it('disables a rule', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + disableRuleRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}/_disable"`); + + alertsClient.disable.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.disable).toHaveBeenCalledTimes(1); + expect(alertsClient.disable.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + disableRuleRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.disable.mockRejectedValue(new AlertTypeDisabledError('Fail', 'license_invalid')); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/disable_rule.ts b/x-pack/plugins/alerting/server/routes/disable_rule.ts new file mode 100644 index 0000000000000..2a2f0f4aa25fc --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/disable_rule.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState, AlertTypeDisabledError } from '../lib'; +import { verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const disableRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id}/_disable`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + try { + await alertsClient.disable({ id }); + return res.noContent(); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/enable_rule.test.ts b/x-pack/plugins/alerting/server/routes/enable_rule.test.ts new file mode 100644 index 0000000000000..71889d153ce5f --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/enable_rule.test.ts @@ -0,0 +1,81 @@ +/* + * 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 { enableRuleRoute } from './enable_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('enableRuleRoute', () => { + it('enables a rule', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + enableRuleRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}/_enable"`); + + alertsClient.enable.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.enable).toHaveBeenCalledTimes(1); + expect(alertsClient.enable.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + enableRuleRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.enable.mockRejectedValue(new AlertTypeDisabledError('Fail', 'license_invalid')); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/enable_rule.ts b/x-pack/plugins/alerting/server/routes/enable_rule.ts new file mode 100644 index 0000000000000..9c7526630d0a3 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/enable_rule.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState, AlertTypeDisabledError } from '../lib'; +import { verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const enableRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id}/_enable`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + try { + await alertsClient.enable({ id }); + return res.noContent(); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/find_rules.test.ts b/x-pack/plugins/alerting/server/routes/find_rules.test.ts new file mode 100644 index 0000000000000..98bb3c77daecc --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/find_rules.test.ts @@ -0,0 +1,148 @@ +/* + * 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 { findRulesRoute } from './find_rules'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('findRulesRoute', () => { + it('finds rules with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rules/_find"`); + + const findResult = { + page: 1, + perPage: 1, + total: 0, + data: [], + }; + alertsClient.find.mockResolvedValueOnce(findResult); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "data": Array [], + "page": 1, + "per_page": 1, + "total": 0, + }, + } + `); + + expect(alertsClient.find).toHaveBeenCalledTimes(1); + expect(alertsClient.find.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "options": Object { + "defaultSearchOperator": "OR", + "page": 1, + "perPage": 1, + }, + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: { + page: 1, + per_page: 1, + total: 0, + data: [], + }, + }); + }); + + it('ensures the license allows finding rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + findRulesRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.find.mockResolvedValueOnce({ + page: 1, + perPage: 1, + total: 0, + data: [], + }); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + } + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents finding rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + findRulesRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + const [context, req, res] = mockHandlerArguments( + {}, + { + query: { + per_page: 1, + page: 1, + default_search_operator: 'OR', + }, + }, + ['ok'] + ); + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/find_rules.ts b/x-pack/plugins/alerting/server/routes/find_rules.ts new file mode 100644 index 0000000000000..06b7960b67297 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/find_rules.ts @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { omit } from 'lodash'; +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState } from '../lib'; +import { FindOptions, FindResult } from '../alerts_client'; +import { RewriteRequestCase, RewriteResponseCase, verifyAccessAndContext } from './lib'; +import { AlertTypeParams, AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +// query definition +const querySchema = schema.object({ + per_page: schema.number({ defaultValue: 10, min: 0 }), + page: schema.number({ defaultValue: 1, min: 1 }), + search: schema.maybe(schema.string()), + default_search_operator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], { + defaultValue: 'OR', + }), + search_fields: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])), + sort_field: schema.maybe(schema.string()), + sort_order: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), + has_reference: schema.maybe( + // use nullable as maybe is currently broken + // in config-schema + schema.nullable( + schema.object({ + type: schema.string(), + id: schema.string(), + }) + ) + ), + fields: schema.maybe(schema.arrayOf(schema.string())), + filter: schema.maybe(schema.string()), +}); + +const rewriteQueryReq: RewriteRequestCase = ({ + default_search_operator: defaultSearchOperator, + has_reference: hasReference, + search_fields: searchFields, + per_page: perPage, + sort_field: sortField, + sort_order: sortOrder, + ...rest +}) => ({ + ...rest, + defaultSearchOperator, + perPage, + ...(sortField ? { sortField } : {}), + ...(sortOrder ? { sortOrder } : {}), + ...(hasReference ? { hasReference } : {}), + ...(searchFields ? { searchFields } : {}), +}); +const rewriteBodyRes: RewriteResponseCase> = ({ + perPage, + data, + ...restOfResult +}) => { + return { + ...restOfResult, + per_page: perPage, + data: data.map( + ({ + alertTypeId, + createdBy, + updatedBy, + createdAt, + updatedAt, + apiKeyOwner, + notifyWhen, + muteAll, + mutedInstanceIds, + executionStatus, + actions, + scheduledTaskId, + ...rest + }) => ({ + ...rest, + rule_type_id: alertTypeId, + created_by: createdBy, + updated_by: updatedBy, + created_at: createdAt, + updated_at: updatedAt, + api_key_owner: apiKeyOwner, + notify_when: notifyWhen, + mute_all: muteAll, + muted_alert_ids: mutedInstanceIds, + scheduled_task_id: scheduledTaskId, + execution_status: executionStatus && { + ...omit(executionStatus, 'lastExecutionDate'), + last_execution_date: executionStatus.lastExecutionDate, + }, + actions: actions.map(({ group, id, actionTypeId, params }) => ({ + group, + id, + params, + connector_type_id: actionTypeId, + })), + }) + ), + }; +}; + +export const findRulesRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${BASE_ALERTING_API_PATH}/rules/_find`, + validate: { + query: querySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + + const options = rewriteQueryReq({ + ...req.query, + has_reference: req.query.has_reference || undefined, + search_fields: searchFieldsAsArray(req.query.search_fields), + }); + + const findResult = await alertsClient.find({ options }); + return res.ok({ + body: rewriteBodyRes(findResult), + }); + }) + ) + ); +}; + +function searchFieldsAsArray(searchFields: string | string[] | undefined): string[] | undefined { + if (!searchFields) { + return; + } + return Array.isArray(searchFields) ? searchFields : [searchFields]; +} diff --git a/x-pack/plugins/alerting/server/routes/get_rule.test.ts b/x-pack/plugins/alerting/server/routes/get_rule.test.ts new file mode 100644 index 0000000000000..fc900797cdc89 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule.test.ts @@ -0,0 +1,169 @@ +/* + * 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 { pick } from 'lodash'; +import { getRuleRoute } from './get_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { SanitizedAlert } from '../types'; +import { AsApiContract } from './lib'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getRuleRoute', () => { + const mockedAlert: SanitizedAlert<{ + bar: boolean; + }> = { + id: '1', + alertTypeId: '1', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date(), + updatedAt: new Date(), + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + consumer: 'bar', + name: 'abc', + tags: ['foo'], + enabled: true, + muteAll: false, + notifyWhen: 'onActionGroupChange', + createdBy: '', + updatedBy: '', + apiKeyOwner: '', + throttle: '30s', + mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + }; + + const getResult: AsApiContract> = { + ...pick(mockedAlert, 'consumer', 'name', 'schedule', 'tags', 'params', 'throttle', 'enabled'), + rule_type_id: mockedAlert.alertTypeId, + notify_when: mockedAlert.notifyWhen, + mute_all: mockedAlert.muteAll, + created_by: mockedAlert.createdBy, + updated_by: mockedAlert.updatedBy, + api_key_owner: mockedAlert.apiKeyOwner, + muted_alert_ids: mockedAlert.mutedInstanceIds, + created_at: mockedAlert.createdAt, + updated_at: mockedAlert.updatedAt, + id: mockedAlert.id, + execution_status: { + status: mockedAlert.executionStatus.status, + last_execution_date: mockedAlert.executionStatus.lastExecutionDate, + }, + actions: [ + { + group: mockedAlert.actions[0].group, + id: mockedAlert.actions[0].id, + params: mockedAlert.actions[0].params, + connector_type_id: mockedAlert.actions[0].actionTypeId, + }, + ], + }; + + it('gets a rule with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleRoute(router, licenseState); + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}"`); + + alertsClient.get.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + await handler(context, req, res); + + expect(alertsClient.get).toHaveBeenCalledTimes(1); + expect(alertsClient.get.mock.calls[0][0].id).toEqual('1'); + + expect(res.ok).toHaveBeenCalledWith({ + body: getResult, + }); + }); + + it('ensures the license allows getting rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.get.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents getting rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + getRuleRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.get.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_rule.ts b/x-pack/plugins/alerting/server/routes/get_rule.ts new file mode 100644 index 0000000000000..fd03f32047e74 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { omit } from 'lodash'; +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; +import { ILicenseState } from '../lib'; +import { verifyAccessAndContext, RewriteResponseCase } from './lib'; +import { + AlertTypeParams, + AlertingRequestHandlerContext, + BASE_ALERTING_API_PATH, + SanitizedAlert, +} from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +const rewriteBodyRes: RewriteResponseCase> = ({ + alertTypeId, + createdBy, + updatedBy, + createdAt, + updatedAt, + apiKeyOwner, + notifyWhen, + muteAll, + mutedInstanceIds, + executionStatus, + actions, + scheduledTaskId, + ...rest +}) => ({ + ...rest, + rule_type_id: alertTypeId, + created_by: createdBy, + updated_by: updatedBy, + created_at: createdAt, + updated_at: updatedAt, + api_key_owner: apiKeyOwner, + notify_when: notifyWhen, + mute_all: muteAll, + muted_alert_ids: mutedInstanceIds, + scheduled_task_id: scheduledTaskId, + execution_status: executionStatus && { + ...omit(executionStatus, 'lastExecutionDate'), + last_execution_date: executionStatus.lastExecutionDate, + }, + actions: actions.map(({ group, id, actionTypeId, params }) => ({ + group, + id, + params, + connector_type_id: actionTypeId, + })), +}); + +export const getRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id}`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + const rule = await alertsClient.get({ id }); + return res.ok({ + body: rewriteBodyRes(rule), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts new file mode 100644 index 0000000000000..fab6d46219a37 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getRuleAlertSummaryRoute } from './get_rule_alert_summary'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertInstanceSummary } from '../types'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getRuleAlertSummaryRoute', () => { + const dateString = new Date().toISOString(); + const mockedAlertInstanceSummary: AlertInstanceSummary = { + id: '', + name: '', + tags: [], + alertTypeId: '', + consumer: '', + muteAll: false, + throttle: null, + enabled: false, + statusStartDate: dateString, + statusEndDate: dateString, + status: 'OK', + errorMessages: [], + instances: {}, + }; + + it('gets rule alert summary', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleAlertSummaryRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rule/{id}/_alert_summary"`); + + alertsClient.getAlertInstanceSummary.mockResolvedValueOnce(mockedAlertInstanceSummary); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + query: {}, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(alertsClient.getAlertInstanceSummary).toHaveBeenCalledTimes(1); + expect(alertsClient.getAlertInstanceSummary.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "dateStart": undefined, + "id": "1", + }, + ] + `); + + expect(res.ok).toHaveBeenCalled(); + }); + + it('returns NOT-FOUND when rule is not found', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleAlertSummaryRoute(router, licenseState); + + const [, handler] = router.get.mock.calls[0]; + + alertsClient.getAlertInstanceSummary = jest + .fn() + .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + query: {}, + }, + ['notFound'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.ts b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.ts new file mode 100644 index 0000000000000..7a3679851d53f --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.ts @@ -0,0 +1,77 @@ +/* + * 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 { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState } from '../lib'; +import { GetAlertInstanceSummaryParams } from '../alerts_client'; +import { RewriteRequestCase, RewriteResponseCase, verifyAccessAndContext } from './lib'; +import { + AlertingRequestHandlerContext, + INTERNAL_BASE_ALERTING_API_PATH, + AlertInstanceSummary, +} from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +const querySchema = schema.object({ + date_start: schema.maybe(schema.string()), +}); + +const rewriteReq: RewriteRequestCase = ({ + date_start: dateStart, + ...rest +}) => ({ + ...rest, + dateStart, +}); +const rewriteBodyRes: RewriteResponseCase = ({ + alertTypeId, + muteAll, + statusStartDate, + statusEndDate, + errorMessages, + lastRun, + instances: alerts, + ...rest +}) => ({ + ...rest, + alerts, + rule_type_id: alertTypeId, + mute_all: muteAll, + status_start_date: statusStartDate, + status_end_date: statusEndDate, + error_messages: errorMessages, + last_run: lastRun, +}); + +export const getRuleAlertSummaryRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/rule/{id}/_alert_summary`, + validate: { + params: paramSchema, + query: querySchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + const summary = await alertsClient.getAlertInstanceSummary( + rewriteReq({ id, ...req.query }) + ); + return res.ok({ body: rewriteBodyRes(summary) }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts new file mode 100644 index 0000000000000..71e06b60d1026 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts @@ -0,0 +1,150 @@ +/* + * 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 { getRuleStateRoute } from './get_rule_state'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; +import { alertsClientMock } from '../alerts_client.mock'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getRuleStateRoute', () => { + const mockedAlertState = { + alertTypeState: { + some: 'value', + }, + alertInstances: { + first_instance: { + state: {}, + meta: { + lastScheduledActions: { + group: 'first_group', + date: new Date(), + }, + }, + }, + second_instance: {}, + }, + }; + + it('gets rule state', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleStateRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rule/{id}/state"`); + + alertsClient.getAlertState.mockResolvedValueOnce(mockedAlertState); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1); + expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.ok).toHaveBeenCalled(); + }); + + it('returns NO-CONTENT when rule exists but has no task state yet', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleStateRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rule/{id}/state"`); + + alertsClient.getAlertState.mockResolvedValueOnce(undefined); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1); + expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('returns NOT-FOUND when rule is not found', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getRuleStateRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rule/{id}/state"`); + + alertsClient.getAlertState = jest + .fn() + .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['notFound'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.getAlertState).toHaveBeenCalledTimes(1); + expect(alertsClient.getAlertState.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/get_rule_state.ts b/x-pack/plugins/alerting/server/routes/get_rule_state.ts new file mode 100644 index 0000000000000..08087d1ece8af --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/get_rule_state.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState } from '../lib'; +import { RewriteResponseCase, verifyAccessAndContext } from './lib'; +import { + AlertingRequestHandlerContext, + INTERNAL_BASE_ALERTING_API_PATH, + AlertTaskState, +} from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +const rewriteBodyRes: RewriteResponseCase = ({ + alertTypeState, + alertInstances, + previousStartedAt, + ...rest +}) => ({ + ...rest, + rule_type_state: alertTypeState, + alerts: alertInstances, + previous_started_at: previousStartedAt, +}); + +export const getRuleStateRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/rule/{id}/state`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + const state = await alertsClient.getAlertState({ id }); + return state ? res.ok({ body: rewriteBodyRes(state) }) : res.noContent(); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/health.test.ts b/x-pack/plugins/alerting/server/routes/health.test.ts index 75c621e4a0abf..be63e0b7054be 100644 --- a/x-pack/plugins/alerting/server/routes/health.test.ts +++ b/x-pack/plugins/alerting/server/routes/health.test.ts @@ -54,7 +54,7 @@ describe('healthRoute', () => { const [config] = router.get.mock.calls[0]; - expect(config.path).toMatchInlineSnapshot(`"/api/alerts/_health"`); + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/_health"`); }); it('queries the usage api', async () => { @@ -107,22 +107,22 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alertingFrameworkHeath: { - decryptionHealth: { + alerting_framework_heath: { + decryption_health: { status: HealthStatus.OK, timestamp: currentDate, }, - executionHealth: { + execution_health: { status: HealthStatus.OK, timestamp: currentDate, }, - readHealth: { + read_health: { status: HealthStatus.OK, timestamp: currentDate, }, }, - hasPermanentEncryptionKey: false, - isSufficientlySecure: true, + has_permanent_encryption_key: false, + is_sufficiently_secure: true, }, }); }); @@ -148,22 +148,22 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alertingFrameworkHeath: { - decryptionHealth: { + alerting_framework_heath: { + decryption_health: { status: HealthStatus.OK, timestamp: currentDate, }, - executionHealth: { + execution_health: { status: HealthStatus.OK, timestamp: currentDate, }, - readHealth: { + read_health: { status: HealthStatus.OK, timestamp: currentDate, }, }, - hasPermanentEncryptionKey: true, - isSufficientlySecure: true, + has_permanent_encryption_key: true, + is_sufficiently_secure: true, }, }); }); @@ -189,22 +189,22 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alertingFrameworkHeath: { - decryptionHealth: { + alerting_framework_heath: { + decryption_health: { status: HealthStatus.OK, timestamp: currentDate, }, - executionHealth: { + execution_health: { status: HealthStatus.OK, timestamp: currentDate, }, - readHealth: { + read_health: { status: HealthStatus.OK, timestamp: currentDate, }, }, - hasPermanentEncryptionKey: true, - isSufficientlySecure: true, + has_permanent_encryption_key: true, + is_sufficiently_secure: true, }, }); }); @@ -230,22 +230,22 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alertingFrameworkHeath: { - decryptionHealth: { + alerting_framework_heath: { + decryption_health: { status: HealthStatus.OK, timestamp: currentDate, }, - executionHealth: { + execution_health: { status: HealthStatus.OK, timestamp: currentDate, }, - readHealth: { + read_health: { status: HealthStatus.OK, timestamp: currentDate, }, }, - hasPermanentEncryptionKey: true, - isSufficientlySecure: false, + has_permanent_encryption_key: true, + is_sufficiently_secure: false, }, }); }); @@ -273,22 +273,22 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alertingFrameworkHeath: { - decryptionHealth: { + alerting_framework_heath: { + decryption_health: { status: HealthStatus.OK, timestamp: currentDate, }, - executionHealth: { + execution_health: { status: HealthStatus.OK, timestamp: currentDate, }, - readHealth: { + read_health: { status: HealthStatus.OK, timestamp: currentDate, }, }, - hasPermanentEncryptionKey: true, - isSufficientlySecure: false, + has_permanent_encryption_key: true, + is_sufficiently_secure: false, }, }); }); @@ -316,22 +316,22 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toStrictEqual({ body: { - alertingFrameworkHeath: { - decryptionHealth: { + alerting_framework_heath: { + decryption_health: { status: HealthStatus.OK, timestamp: currentDate, }, - executionHealth: { + execution_health: { status: HealthStatus.OK, timestamp: currentDate, }, - readHealth: { + read_health: { status: HealthStatus.OK, timestamp: currentDate, }, }, - hasPermanentEncryptionKey: true, - isSufficientlySecure: true, + has_permanent_encryption_key: true, + is_sufficiently_secure: true, }, }); }); diff --git a/x-pack/plugins/alerting/server/routes/health.ts b/x-pack/plugins/alerting/server/routes/health.ts index de0b14465c5ac..c2a122a28fa49 100644 --- a/x-pack/plugins/alerting/server/routes/health.ts +++ b/x-pack/plugins/alerting/server/routes/health.ts @@ -6,11 +6,15 @@ */ import { ApiResponse } from '@elastic/elasticsearch'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { AlertingFrameworkHealth } from '../types'; +import { IRouter } from 'kibana/server'; +import { ILicenseState } from '../lib'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; +import { RewriteResponseCase, verifyAccessAndContext } from './lib'; +import { + AlertingRequestHandlerContext, + BASE_ALERTING_API_PATH, + AlertingFrameworkHealth, +} from '../types'; interface XPackUsageSecurity { security?: { @@ -23,49 +27,63 @@ interface XPackUsageSecurity { }; } -export function healthRoute( - router: AlertingRouter, +const rewriteBodyRes: RewriteResponseCase = ({ + isSufficientlySecure, + hasPermanentEncryptionKey, + alertingFrameworkHeath, + ...rest +}) => ({ + ...rest, + is_sufficiently_secure: isSufficientlySecure, + has_permanent_encryption_key: hasPermanentEncryptionKey, + alerting_framework_heath: { + decryption_health: alertingFrameworkHeath.decryptionHealth, + execution_health: alertingFrameworkHeath.executionHealth, + read_health: alertingFrameworkHeath.readHealth, + }, +}); + +export const healthRoute = ( + router: IRouter, licenseState: ILicenseState, encryptedSavedObjects: EncryptedSavedObjectsPluginSetup -) { +) => { router.get( { - path: '/api/alerts/_health', + path: `${BASE_ALERTING_API_PATH}/_health`, validate: false, }, - router.handleLegacyErrors(async function (context, req, res) { - verifyApiAccess(licenseState); - if (!context.alerting) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); - } - try { - const { - body: { - security: { - enabled: isSecurityEnabled = false, - ssl: { http: { enabled: isTLSEnabled = false } = {} } = {}, - } = {}, - }, - }: ApiResponse = await context.core.elasticsearch.client.asInternalUser.transport // Do not augment with such input. // `transport.request` is potentially unsafe when combined with untrusted user input. - .request({ - method: 'GET', - path: '/_xpack/usage', - }); + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + try { + const { + body: { + security: { + enabled: isSecurityEnabled = false, + ssl: { http: { enabled: isTLSEnabled = false } = {} } = {}, + } = {}, + }, + }: ApiResponse = await context.core.elasticsearch.client.asInternalUser.transport // Do not augment with such input. // `transport.request` is potentially unsafe when combined with untrusted user input. + .request({ + method: 'GET', + path: '/_xpack/usage', + }); - const alertingFrameworkHeath = await context.alerting.getFrameworkHealth(); + const alertingFrameworkHeath = await context.alerting.getFrameworkHealth(); - const frameworkHealth: AlertingFrameworkHealth = { - isSufficientlySecure: !isSecurityEnabled || (isSecurityEnabled && isTLSEnabled), - hasPermanentEncryptionKey: encryptedSavedObjects.canEncrypt, - alertingFrameworkHeath, - }; + const frameworkHealth: AlertingFrameworkHealth = { + isSufficientlySecure: !isSecurityEnabled || (isSecurityEnabled && isTLSEnabled), + hasPermanentEncryptionKey: encryptedSavedObjects.canEncrypt, + alertingFrameworkHeath, + }; - return res.ok({ - body: frameworkHealth, - }); - } catch (error) { - return res.badRequest({ body: error }); - } - }) + return res.ok({ + body: rewriteBodyRes(frameworkHealth), + }); + } catch (error) { + return res.badRequest({ body: error }); + } + }) + ) ); -} +}; diff --git a/x-pack/plugins/alerting/server/routes/index.ts b/x-pack/plugins/alerting/server/routes/index.ts index e250d17866a2b..c6f12ffba2f20 100644 --- a/x-pack/plugins/alerting/server/routes/index.ts +++ b/x-pack/plugins/alerting/server/routes/index.ts @@ -5,20 +5,50 @@ * 2.0. */ -export { aggregateAlertRoute } from './aggregate'; -export { createAlertRoute } from './create'; -export { deleteAlertRoute } from './delete'; -export { findAlertRoute } from './find'; -export { getAlertRoute } from './get'; -export { getAlertStateRoute } from './get_alert_state'; -export { getAlertInstanceSummaryRoute } from './get_alert_instance_summary'; -export { listAlertTypesRoute } from './list_alert_types'; -export { updateAlertRoute } from './update'; -export { enableAlertRoute } from './enable'; -export { disableAlertRoute } from './disable'; -export { updateApiKeyRoute } from './update_api_key'; -export { muteAlertInstanceRoute } from './mute_instance'; -export { unmuteAlertInstanceRoute } from './unmute_instance'; -export { muteAllAlertRoute } from './mute_all'; -export { unmuteAllAlertRoute } from './unmute_all'; -export { healthRoute } from './health'; +import { IRouter } from 'kibana/server'; +import { ILicenseState } from '../lib'; +import { defineLegacyRoutes } from './legacy'; +import { AlertingRequestHandlerContext } from '../types'; +import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; +import { createRuleRoute } from './create_rule'; +import { getRuleRoute } from './get_rule'; +import { updateRuleRoute } from './update_rule'; +import { deleteRuleRoute } from './delete_rule'; +import { aggregateRulesRoute } from './aggregate_rules'; +import { disableRuleRoute } from './disable_rule'; +import { enableRuleRoute } from './enable_rule'; +import { findRulesRoute } from './find_rules'; +import { getRuleAlertSummaryRoute } from './get_rule_alert_summary'; +import { getRuleStateRoute } from './get_rule_state'; +import { healthRoute } from './health'; +import { ruleTypesRoute } from './rule_types'; +import { muteAllRuleRoute } from './mute_all_rule'; +import { muteAlertRoute } from './mute_alert'; +import { unmuteAllRuleRoute } from './unmute_all_rule'; +import { unmuteAlertRoute } from './unmute_alert'; +import { updateRuleApiKeyRoute } from './update_rule_api_key'; + +export function defineRoutes( + router: IRouter, + licenseState: ILicenseState, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { + defineLegacyRoutes(router, licenseState, encryptedSavedObjects); + createRuleRoute(router, licenseState); + getRuleRoute(router, licenseState); + updateRuleRoute(router, licenseState); + deleteRuleRoute(router, licenseState); + aggregateRulesRoute(router, licenseState); + disableRuleRoute(router, licenseState); + enableRuleRoute(router, licenseState); + findRulesRoute(router, licenseState); + getRuleAlertSummaryRoute(router, licenseState); + getRuleStateRoute(router, licenseState); + healthRoute(router, licenseState, encryptedSavedObjects); + ruleTypesRoute(router, licenseState); + muteAllRuleRoute(router, licenseState); + muteAlertRoute(router, licenseState); + unmuteAllRuleRoute(router, licenseState); + unmuteAlertRoute(router, licenseState); + updateRuleApiKeyRoute(router, licenseState); +} diff --git a/x-pack/plugins/alerting/server/routes/aggregate.test.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts similarity index 91% rename from x-pack/plugins/alerting/server/routes/aggregate.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts index 60e9a2a1bcc79..94331902d9ace 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.test.ts @@ -7,14 +7,14 @@ import { aggregateAlertRoute } from './aggregate'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/aggregate.ts b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts similarity index 83% rename from x-pack/plugins/alerting/server/routes/aggregate.ts rename to x-pack/plugins/alerting/server/routes/legacy/aggregate.ts index 1416f60277daa..91189fdf3d0a6 100644 --- a/x-pack/plugins/alerting/server/routes/aggregate.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/aggregate.ts @@ -6,12 +6,12 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { renameKeys } from './lib/rename_keys'; -import { FindOptions } from '../alerts_client'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { renameKeys } from './../lib/rename_keys'; +import { FindOptions } from '../../alerts_client'; // config definition const querySchema = schema.object({ @@ -36,7 +36,7 @@ const querySchema = schema.object({ export const aggregateAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/_aggregate`, + path: `${LEGACY_BASE_ALERT_API_PATH}/_aggregate`, validate: { query: querySchema, }, diff --git a/x-pack/plugins/alerting/server/routes/create.test.ts b/x-pack/plugins/alerting/server/routes/legacy/create.test.ts similarity index 93% rename from x-pack/plugins/alerting/server/routes/create.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/create.test.ts index 1a2dfec8612e3..fd3252d2fca77 100644 --- a/x-pack/plugins/alerting/server/routes/create.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/create.test.ts @@ -7,16 +7,16 @@ import { createAlertRoute } from './create'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { Alert } from '../../common/alert'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { Alert } from '../../../common/alert'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/create.ts b/x-pack/plugins/alerting/server/routes/legacy/create.ts similarity index 75% rename from x-pack/plugins/alerting/server/routes/create.ts rename to x-pack/plugins/alerting/server/routes/legacy/create.ts index 7b1a518112ddd..fca2b67118527 100644 --- a/x-pack/plugins/alerting/server/routes/create.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/create.ts @@ -6,19 +6,19 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { validateDurationSchema } from '../lib'; -import { handleDisabledApiKeysError } from './lib/error_handler'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { validateDurationSchema } from '../../lib'; +import { handleDisabledApiKeysError } from './../lib/error_handler'; import { - Alert, + SanitizedAlert, AlertNotifyWhenType, AlertTypeParams, - BASE_ALERT_API_PATH, + LEGACY_BASE_ALERT_API_PATH, validateNotifyWhenType, -} from '../types'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +} from '../../types'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; export const bodySchema = schema.object({ name: schema.string(), @@ -46,7 +46,7 @@ export const bodySchema = schema.object({ export const createAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{id?}`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id?}`, validate: { params: schema.maybe( schema.object({ @@ -68,10 +68,12 @@ export const createAlertRoute = (router: AlertingRouter, licenseState: ILicenseS const params = req.params; const notifyWhen = alert?.notifyWhen ? (alert.notifyWhen as AlertNotifyWhenType) : null; try { - const alertRes: Alert = await alertsClient.create({ - data: { ...alert, notifyWhen }, - options: { id: params?.id }, - }); + const alertRes: SanitizedAlert = await alertsClient.create( + { + data: { ...alert, notifyWhen }, + options: { id: params?.id }, + } + ); return res.ok({ body: alertRes, }); diff --git a/x-pack/plugins/alerting/server/routes/delete.test.ts b/x-pack/plugins/alerting/server/routes/legacy/delete.test.ts similarity index 89% rename from x-pack/plugins/alerting/server/routes/delete.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/delete.test.ts index 47564f67d42a5..e71b2788b98c7 100644 --- a/x-pack/plugins/alerting/server/routes/delete.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/delete.test.ts @@ -7,14 +7,14 @@ import { deleteAlertRoute } from './delete'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/delete.ts b/x-pack/plugins/alerting/server/routes/legacy/delete.ts similarity index 76% rename from x-pack/plugins/alerting/server/routes/delete.ts rename to x-pack/plugins/alerting/server/routes/legacy/delete.ts index e217fd0533771..650126be4499d 100644 --- a/x-pack/plugins/alerting/server/routes/delete.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/delete.ts @@ -6,10 +6,10 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; const paramSchema = schema.object({ id: schema.string(), @@ -18,7 +18,7 @@ const paramSchema = schema.object({ export const deleteAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.delete( { - path: `${BASE_ALERT_API_PATH}/alert/{id}`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/disable.test.ts b/x-pack/plugins/alerting/server/routes/legacy/disable.test.ts similarity index 86% rename from x-pack/plugins/alerting/server/routes/disable.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/disable.test.ts index 347b1641515e6..34c0af711a338 100644 --- a/x-pack/plugins/alerting/server/routes/disable.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/disable.test.ts @@ -7,14 +7,14 @@ import { disableAlertRoute } from './disable'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/disable.ts b/x-pack/plugins/alerting/server/routes/legacy/disable.ts similarity index 74% rename from x-pack/plugins/alerting/server/routes/disable.ts rename to x-pack/plugins/alerting/server/routes/legacy/disable.ts index 7129fbdd52698..140e0492fbaab 100644 --- a/x-pack/plugins/alerting/server/routes/disable.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/disable.ts @@ -6,11 +6,11 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const paramSchema = schema.object({ id: schema.string(), @@ -19,7 +19,7 @@ const paramSchema = schema.object({ export const disableAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{id}/_disable`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}/_disable`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/enable.test.ts b/x-pack/plugins/alerting/server/routes/legacy/enable.test.ts similarity index 86% rename from x-pack/plugins/alerting/server/routes/enable.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/enable.test.ts index c12a19fc35dbe..88229472a2936 100644 --- a/x-pack/plugins/alerting/server/routes/enable.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/enable.test.ts @@ -7,14 +7,14 @@ import { enableAlertRoute } from './enable'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/enable.ts b/x-pack/plugins/alerting/server/routes/legacy/enable.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/enable.ts rename to x-pack/plugins/alerting/server/routes/legacy/enable.ts index d874e9b6106b9..fcf9ceb8a058b 100644 --- a/x-pack/plugins/alerting/server/routes/enable.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/enable.ts @@ -6,12 +6,12 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { handleDisabledApiKeysError } from './lib/error_handler'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { handleDisabledApiKeysError } from './../lib/error_handler'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const paramSchema = schema.object({ id: schema.string(), @@ -20,7 +20,7 @@ const paramSchema = schema.object({ export const enableAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{id}/_enable`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}/_enable`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/find.test.ts b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts similarity index 91% rename from x-pack/plugins/alerting/server/routes/find.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/find.test.ts index 791e2babc062e..640451afcca97 100644 --- a/x-pack/plugins/alerting/server/routes/find.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.test.ts @@ -7,14 +7,14 @@ import { findAlertRoute } from './find'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts similarity index 86% rename from x-pack/plugins/alerting/server/routes/find.ts rename to x-pack/plugins/alerting/server/routes/legacy/find.ts index ad345de685266..1d54df53d883a 100644 --- a/x-pack/plugins/alerting/server/routes/find.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts @@ -6,13 +6,13 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; +import type { AlertingRouter } from '../../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { renameKeys } from './lib/rename_keys'; -import { FindOptions } from '../alerts_client'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { renameKeys } from './../lib/rename_keys'; +import { FindOptions } from '../../alerts_client'; // config definition const querySchema = schema.object({ @@ -42,7 +42,7 @@ const querySchema = schema.object({ export const findAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/_find`, + path: `${LEGACY_BASE_ALERT_API_PATH}/_find`, validate: { query: querySchema, }, diff --git a/x-pack/plugins/alerting/server/routes/get.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get.test.ts similarity index 90% rename from x-pack/plugins/alerting/server/routes/get.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/get.test.ts index 79938e572dfcf..b2704cdd234f1 100644 --- a/x-pack/plugins/alerting/server/routes/get.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get.test.ts @@ -7,14 +7,14 @@ import { getAlertRoute } from './get'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { Alert } from '../../common'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { Alert } from '../../../common'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/get.ts b/x-pack/plugins/alerting/server/routes/legacy/get.ts similarity index 76% rename from x-pack/plugins/alerting/server/routes/get.ts rename to x-pack/plugins/alerting/server/routes/legacy/get.ts index 93d2f29f53c18..cf63a4387a93e 100644 --- a/x-pack/plugins/alerting/server/routes/get.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get.ts @@ -6,10 +6,10 @@ */ import { schema } from '@kbn/config-schema'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import type { AlertingRouter } from '../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import type { AlertingRouter } from '../../types'; const paramSchema = schema.object({ id: schema.string(), @@ -18,7 +18,7 @@ const paramSchema = schema.object({ export const getAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/alert/{id}`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/get_alert_instance_summary.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts similarity index 89% rename from x-pack/plugins/alerting/server/routes/get_alert_instance_summary.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts index 3157f18afbb95..0162ef91f55e7 100644 --- a/x-pack/plugins/alerting/server/routes/get_alert_instance_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts @@ -7,14 +7,14 @@ import { getAlertInstanceSummaryRoute } from './get_alert_instance_summary'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; import { SavedObjectsErrorHelpers } from 'src/core/server'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertInstanceSummary } from '../types'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertInstanceSummary } from '../../types'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/get_alert_instance_summary.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts similarity index 79% rename from x-pack/plugins/alerting/server/routes/get_alert_instance_summary.ts rename to x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts index 71c85caa38c8d..00c96197f6f9b 100644 --- a/x-pack/plugins/alerting/server/routes/get_alert_instance_summary.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts @@ -6,10 +6,10 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; const paramSchema = schema.object({ id: schema.string(), @@ -25,7 +25,7 @@ export const getAlertInstanceSummaryRoute = ( ) => { router.get( { - path: `${BASE_ALERT_API_PATH}/alert/{id}/_instance_summary`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}/_instance_summary`, validate: { params: paramSchema, query: querySchema, diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts similarity index 93% rename from x-pack/plugins/alerting/server/routes/get_alert_state.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts index 5aa96d6c73447..f08e0992cbba0 100644 --- a/x-pack/plugins/alerting/server/routes/get_alert_state.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts @@ -7,13 +7,13 @@ import { getAlertStateRoute } from './get_alert_state'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; import { SavedObjectsErrorHelpers } from 'src/core/server'; -import { alertsClientMock } from '../alerts_client.mock'; +import { alertsClientMock } from '../../alerts_client.mock'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/get_alert_state.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts similarity index 77% rename from x-pack/plugins/alerting/server/routes/get_alert_state.ts rename to x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts index 3a05946162d37..5e7cbfbe6eb95 100644 --- a/x-pack/plugins/alerting/server/routes/get_alert_state.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts @@ -6,10 +6,10 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; const paramSchema = schema.object({ id: schema.string(), @@ -18,7 +18,7 @@ const paramSchema = schema.object({ export const getAlertStateRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/alert/{id}/state`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}/state`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/health.test.ts b/x-pack/plugins/alerting/server/routes/legacy/health.test.ts new file mode 100644 index 0000000000000..74de5f70a32e7 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/legacy/health.test.ts @@ -0,0 +1,336 @@ +/* + * 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 { healthRoute } from './health'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { elasticsearchServiceMock } from '../../../../../../src/core/server/mocks'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { HealthStatus } from '../../types'; +import { alertsMock } from '../../mocks'; +const alertsClient = alertsClientMock.create(); + +jest.mock('../../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +const alerting = alertsMock.createStart(); + +const currentDate = new Date().toISOString(); +beforeEach(() => { + jest.resetAllMocks(); + alerting.getFrameworkHealth.mockResolvedValue({ + decryptionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + readHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + }); +}); + +describe('healthRoute', () => { + it('registers the route', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + healthRoute(router, licenseState, encryptedSavedObjects); + + const [config] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerts/_health"`); + }); + + it('queries the usage api', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asInternalUser.transport.request.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({}) + ); + + const [context, req, res] = mockHandlerArguments({ esClient, alertsClient }, {}, ['ok']); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + + expect(esClient.asInternalUser.transport.request.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "method": "GET", + "path": "/_xpack/usage", + }, + ] + `); + }); + + it('evaluates whether Encrypted Saved Objects is missing encryption key', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: false }); + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asInternalUser.transport.request.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({}) + ); + + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); + + expect(await handler(context, req, res)).toStrictEqual({ + body: { + alertingFrameworkHeath: { + decryptionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + readHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + }, + hasPermanentEncryptionKey: false, + isSufficientlySecure: true, + }, + }); + }); + + it('evaluates missing security info from the usage api to mean that the security plugin is disbled', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asInternalUser.transport.request.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({}) + ); + + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); + + expect(await handler(context, req, res)).toStrictEqual({ + body: { + alertingFrameworkHeath: { + decryptionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + readHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + }, + hasPermanentEncryptionKey: true, + isSufficientlySecure: true, + }, + }); + }); + + it('evaluates missing security http info from the usage api to mean that the security plugin is disbled', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asInternalUser.transport.request.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({}) + ); + + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); + + expect(await handler(context, req, res)).toStrictEqual({ + body: { + alertingFrameworkHeath: { + decryptionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + readHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + }, + hasPermanentEncryptionKey: true, + isSufficientlySecure: true, + }, + }); + }); + + it('evaluates security enabled, and missing ssl info from the usage api to mean that the user cannot generate keys', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asInternalUser.transport.request.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ security: { enabled: true } }) + ); + + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); + + expect(await handler(context, req, res)).toStrictEqual({ + body: { + alertingFrameworkHeath: { + decryptionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + readHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + }, + hasPermanentEncryptionKey: true, + isSufficientlySecure: false, + }, + }); + }); + + it('evaluates security enabled, SSL info present but missing http info from the usage api to mean that the user cannot generate keys', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asInternalUser.transport.request.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + security: { enabled: true, ssl: {} }, + }) + ); + + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); + + expect(await handler(context, req, res)).toStrictEqual({ + body: { + alertingFrameworkHeath: { + decryptionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + readHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + }, + hasPermanentEncryptionKey: true, + isSufficientlySecure: false, + }, + }); + }); + + it('evaluates security and tls enabled to mean that the user can generate keys', async () => { + const router = httpServiceMock.createRouter(); + + const licenseState = licenseStateMock.create(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true }); + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + esClient.asInternalUser.transport.request.mockReturnValue( + elasticsearchServiceMock.createSuccessTransportRequestPromise({ + security: { enabled: true, ssl: { http: { enabled: true } } }, + }) + ); + + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); + + expect(await handler(context, req, res)).toStrictEqual({ + body: { + alertingFrameworkHeath: { + decryptionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + readHealth: { + status: HealthStatus.OK, + timestamp: currentDate, + }, + }, + hasPermanentEncryptionKey: true, + isSufficientlySecure: true, + }, + }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/legacy/health.ts b/x-pack/plugins/alerting/server/routes/legacy/health.ts new file mode 100644 index 0000000000000..b9906a56ce972 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/legacy/health.ts @@ -0,0 +1,71 @@ +/* + * 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 { ApiResponse } from '@elastic/elasticsearch'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { AlertingFrameworkHealth } from '../../types'; +import { EncryptedSavedObjectsPluginSetup } from '../../../../encrypted_saved_objects/server'; + +interface XPackUsageSecurity { + security?: { + enabled?: boolean; + ssl?: { + http?: { + enabled?: boolean; + }; + }; + }; +} + +export function healthRoute( + router: AlertingRouter, + licenseState: ILicenseState, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { + router.get( + { + path: '/api/alerts/_health', + validate: false, + }, + router.handleLegacyErrors(async function (context, req, res) { + verifyApiAccess(licenseState); + if (!context.alerting) { + return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); + } + try { + const { + body: { + security: { + enabled: isSecurityEnabled = false, + ssl: { http: { enabled: isTLSEnabled = false } = {} } = {}, + } = {}, + }, + }: ApiResponse = await context.core.elasticsearch.client.asInternalUser.transport // Do not augment with such input. // `transport.request` is potentially unsafe when combined with untrusted user input. + .request({ + method: 'GET', + path: '/_xpack/usage', + }); + + const alertingFrameworkHeath = await context.alerting.getFrameworkHealth(); + + const frameworkHealth: AlertingFrameworkHealth = { + isSufficientlySecure: !isSecurityEnabled || (isSecurityEnabled && isTLSEnabled), + hasPermanentEncryptionKey: encryptedSavedObjects.canEncrypt, + alertingFrameworkHeath, + }; + + return res.ok({ + body: frameworkHealth, + }); + } catch (error) { + return res.badRequest({ body: error }); + } + }) + ); +} diff --git a/x-pack/plugins/alerting/server/routes/legacy/index.ts b/x-pack/plugins/alerting/server/routes/legacy/index.ts new file mode 100644 index 0000000000000..d1b2f9784f24d --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/legacy/index.ts @@ -0,0 +1,53 @@ +/* + * 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 { IRouter } from 'kibana/server'; +import { ILicenseState } from '../../lib'; +import { AlertingRequestHandlerContext } from '../../types'; +import { EncryptedSavedObjectsPluginSetup } from '../../../../encrypted_saved_objects/server'; + +import { aggregateAlertRoute } from './aggregate'; +import { createAlertRoute } from './create'; +import { deleteAlertRoute } from './delete'; +import { findAlertRoute } from './find'; +import { getAlertRoute } from './get'; +import { getAlertStateRoute } from './get_alert_state'; +import { getAlertInstanceSummaryRoute } from './get_alert_instance_summary'; +import { listAlertTypesRoute } from './list_alert_types'; +import { updateAlertRoute } from './update'; +import { enableAlertRoute } from './enable'; +import { disableAlertRoute } from './disable'; +import { updateApiKeyRoute } from './update_api_key'; +import { muteAlertInstanceRoute } from './mute_instance'; +import { unmuteAlertInstanceRoute } from './unmute_instance'; +import { muteAllAlertRoute } from './mute_all'; +import { unmuteAllAlertRoute } from './unmute_all'; +import { healthRoute } from './health'; + +export function defineLegacyRoutes( + router: IRouter, + licenseState: ILicenseState, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { + aggregateAlertRoute(router, licenseState); + createAlertRoute(router, licenseState); + deleteAlertRoute(router, licenseState); + findAlertRoute(router, licenseState); + getAlertRoute(router, licenseState); + getAlertStateRoute(router, licenseState); + getAlertInstanceSummaryRoute(router, licenseState); + listAlertTypesRoute(router, licenseState); + updateAlertRoute(router, licenseState); + enableAlertRoute(router, licenseState); + disableAlertRoute(router, licenseState); + updateApiKeyRoute(router, licenseState); + muteAllAlertRoute(router, licenseState); + unmuteAllAlertRoute(router, licenseState); + muteAlertInstanceRoute(router, licenseState); + unmuteAlertInstanceRoute(router, licenseState); + healthRoute(router, licenseState, encryptedSavedObjects); +} diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.test.ts similarity index 92% rename from x-pack/plugins/alerting/server/routes/list_alert_types.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/list_alert_types.test.ts index 5ef2e8cc9ec7d..3e6f2f484a6d8 100644 --- a/x-pack/plugins/alerting/server/routes/list_alert_types.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.test.ts @@ -7,16 +7,16 @@ import { listAlertTypesRoute } from './list_alert_types'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { RecoveredActionGroup } from '../../common'; -import { RegistryAlertTypeWithAuth } from '../authorization'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { RecoveredActionGroup } from '../../../common'; +import { RegistryAlertTypeWithAuth } from '../../authorization'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/list_alert_types.ts b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/list_alert_types.ts rename to x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts index 2040f71f8ad05..da41cc487228c 100644 --- a/x-pack/plugins/alerting/server/routes/list_alert_types.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts @@ -5,15 +5,15 @@ * 2.0. */ -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; export const listAlertTypesRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { - path: `${BASE_ALERT_API_PATH}/list_alert_types`, + path: `${LEGACY_BASE_ALERT_API_PATH}/list_alert_types`, validate: {}, }, router.handleLegacyErrors(async function (context, req, res) { diff --git a/x-pack/plugins/alerting/server/routes/mute_all.test.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_all.test.ts similarity index 86% rename from x-pack/plugins/alerting/server/routes/mute_all.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/mute_all.test.ts index 8c938d71ab06c..ffe893fa60490 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_all.test.ts @@ -7,13 +7,13 @@ import { muteAllAlertRoute } from './mute_all'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/mute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts similarity index 74% rename from x-pack/plugins/alerting/server/routes/mute_all.ts rename to x-pack/plugins/alerting/server/routes/legacy/mute_all.ts index fee1fdfe5a4f7..643aeb97084a8 100644 --- a/x-pack/plugins/alerting/server/routes/mute_all.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts @@ -6,11 +6,11 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const paramSchema = schema.object({ id: schema.string(), @@ -19,7 +19,7 @@ const paramSchema = schema.object({ export const muteAllAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{id}/_mute_all`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}/_mute_all`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.test.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.test.ts similarity index 87% rename from x-pack/plugins/alerting/server/routes/mute_instance.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/mute_instance.test.ts index 83bf57f5259a3..a00c75663c5f0 100644 --- a/x-pack/plugins/alerting/server/routes/mute_instance.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.test.ts @@ -7,13 +7,13 @@ import { muteAlertInstanceRoute } from './mute_instance'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/mute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/mute_instance.ts rename to x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts index ab7749178f6cf..2b35f59c7fce1 100644 --- a/x-pack/plugins/alerting/server/routes/mute_instance.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts @@ -6,13 +6,13 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { renameKeys } from './lib/rename_keys'; -import { MuteOptions } from '../alerts_client'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { renameKeys } from './../lib/rename_keys'; +import { MuteOptions } from '../../alerts_client'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const paramSchema = schema.object({ alert_id: schema.string(), @@ -22,7 +22,7 @@ const paramSchema = schema.object({ export const muteAlertInstanceRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.test.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.test.ts similarity index 86% rename from x-pack/plugins/alerting/server/routes/unmute_all.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/unmute_all.test.ts index 0c2d3105e581c..8511a8b68a447 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_all.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.test.ts @@ -7,13 +7,13 @@ import { unmuteAllAlertRoute } from './unmute_all'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/unmute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts similarity index 74% rename from x-pack/plugins/alerting/server/routes/unmute_all.ts rename to x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts index 2e67884f8a944..1259428be3329 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_all.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts @@ -6,11 +6,11 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const paramSchema = schema.object({ id: schema.string(), @@ -19,7 +19,7 @@ const paramSchema = schema.object({ export const unmuteAllAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{id}/_unmute_all`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}/_unmute_all`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.test.ts similarity index 87% rename from x-pack/plugins/alerting/server/routes/unmute_instance.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/unmute_instance.test.ts index eddd00390a7f3..d28868a3c9230 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_instance.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.test.ts @@ -7,13 +7,13 @@ import { unmuteAlertInstanceRoute } from './unmute_instance'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/unmute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts similarity index 74% rename from x-pack/plugins/alerting/server/routes/unmute_instance.ts rename to x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts index d39fc696eef08..25f5aa654bde1 100644 --- a/x-pack/plugins/alerting/server/routes/unmute_instance.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts @@ -6,11 +6,11 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const paramSchema = schema.object({ alertId: schema.string(), @@ -20,7 +20,7 @@ const paramSchema = schema.object({ export const unmuteAlertInstanceRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/update.test.ts b/x-pack/plugins/alerting/server/routes/legacy/update.test.ts similarity index 92% rename from x-pack/plugins/alerting/server/routes/update.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/update.test.ts index 892f1a25844c4..da58d0dc41522 100644 --- a/x-pack/plugins/alerting/server/routes/update.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update.test.ts @@ -7,15 +7,15 @@ import { updateAlertRoute } from './update'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; -import { AlertNotifyWhenType } from '../../common'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; +import { AlertNotifyWhenType } from '../../../common'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/update.ts b/x-pack/plugins/alerting/server/routes/legacy/update.ts similarity index 81% rename from x-pack/plugins/alerting/server/routes/update.ts rename to x-pack/plugins/alerting/server/routes/legacy/update.ts index 11373e71d14be..23a0719319e00 100644 --- a/x-pack/plugins/alerting/server/routes/update.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update.ts @@ -6,13 +6,17 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { validateDurationSchema } from '../lib'; -import { handleDisabledApiKeysError } from './lib/error_handler'; -import { AlertNotifyWhenType, BASE_ALERT_API_PATH, validateNotifyWhenType } from '../../common'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { validateDurationSchema } from '../../lib'; +import { handleDisabledApiKeysError } from './../lib/error_handler'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; +import { + AlertNotifyWhenType, + LEGACY_BASE_ALERT_API_PATH, + validateNotifyWhenType, +} from '../../../common'; const paramSchema = schema.object({ id: schema.string(), @@ -41,7 +45,7 @@ const bodySchema = schema.object({ export const updateAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.put( { - path: `${BASE_ALERT_API_PATH}/alert/{id}`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}`, validate: { body: bodySchema, params: paramSchema, diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.test.ts b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.test.ts similarity index 86% rename from x-pack/plugins/alerting/server/routes/update_api_key.test.ts rename to x-pack/plugins/alerting/server/routes/legacy/update_api_key.test.ts index 0added369fd61..3f556f480f69c 100644 --- a/x-pack/plugins/alerting/server/routes/update_api_key.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.test.ts @@ -7,13 +7,13 @@ import { updateApiKeyRoute } from './update_api_key'; import { httpServiceMock } from 'src/core/server/mocks'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { mockHandlerArguments } from './_mock_handler_arguments'; -import { alertsClientMock } from '../alerts_client.mock'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { licenseStateMock } from '../../lib/license_state.mock'; +import { mockHandlerArguments } from './../_mock_handler_arguments'; +import { alertsClientMock } from '../../alerts_client.mock'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const alertsClient = alertsClientMock.create(); -jest.mock('../lib/license_api_access.ts', () => ({ +jest.mock('../../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); diff --git a/x-pack/plugins/alerting/server/routes/update_api_key.ts b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts similarity index 72% rename from x-pack/plugins/alerting/server/routes/update_api_key.ts rename to x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts index a615ee8a5bbd2..a4da2538b0bf9 100644 --- a/x-pack/plugins/alerting/server/routes/update_api_key.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts @@ -6,12 +6,12 @@ */ import { schema } from '@kbn/config-schema'; -import type { AlertingRouter } from '../types'; -import { ILicenseState } from '../lib/license_state'; -import { verifyApiAccess } from '../lib/license_api_access'; -import { BASE_ALERT_API_PATH } from '../../common'; -import { handleDisabledApiKeysError } from './lib/error_handler'; -import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import type { AlertingRouter } from '../../types'; +import { ILicenseState } from '../../lib/license_state'; +import { verifyApiAccess } from '../../lib/license_api_access'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../common'; +import { handleDisabledApiKeysError } from './../lib/error_handler'; +import { AlertTypeDisabledError } from '../../lib/errors/alert_type_disabled'; const paramSchema = schema.object({ id: schema.string(), @@ -20,7 +20,7 @@ const paramSchema = schema.object({ export const updateApiKeyRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { - path: `${BASE_ALERT_API_PATH}/alert/{id}/_update_api_key`, + path: `${LEGACY_BASE_ALERT_API_PATH}/alert/{id}/_update_api_key`, validate: { params: paramSchema, }, diff --git a/x-pack/plugins/alerting/server/routes/lib/index.ts b/x-pack/plugins/alerting/server/routes/lib/index.ts new file mode 100644 index 0000000000000..142513e23e5e7 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { + handleDisabledApiKeysError, + isApiKeyDisabledError, + isSecurityPluginDisabledError, +} from './error_handler'; +export { renameKeys } from './rename_keys'; +export { AsApiContract, RewriteRequestCase, RewriteResponseCase } from './rewrite_request_case'; +export { verifyAccessAndContext } from './verify_access_and_context'; diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts new file mode 100644 index 0000000000000..361ba5ff5e55d --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_request_case.ts @@ -0,0 +1,82 @@ +/* + * 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 { JsonValue } from '../../../../../../src/plugins/kibana_utils/common'; + +type RenameAlertToRule = K extends `alertTypeId` + ? `ruleTypeId` + : K extends `alertId` + ? `ruleId` + : K extends `alertExecutionStatus` + ? `ruleExecutionStatus` + : K extends `actionTypeId` + ? `connectorTypeId` + : K extends `alertInstanceId` + ? `alertId` + : K extends `mutedInstanceIds` + ? `mutedAlertIds` + : K extends `instances` + ? `alerts` + : K; + +export type AsApiContract< + T, + ComplexPropertyKeys = `actions` | `executionStatus`, + OpaquePropertyKeys = `params` +> = T extends Array + ? Array> + : { + [K in keyof T as CamelToSnake< + RenameAlertToRule> + >]: K extends OpaquePropertyKeys + ? // don't convert explciitly opaque types which we treat as a black box + T[K] + : T[K] extends undefined + ? AsApiContract> | undefined + : // don't convert built in types + T[K] extends Date | JsonValue + ? T[K] + : T[K] extends Array + ? Array> + : K extends ComplexPropertyKeys + ? AsApiContract + : T[K] extends object + ? AsApiContract + : // don't convert anything else + T[K]; + }; + +export type RewriteRequestCase = (requested: AsApiContract) => T; +export type RewriteResponseCase = ( + responded: T +) => T extends Array ? Array> : AsApiContract; + +/** + * This type maps Camel Case strings into their Snake Case version. + * This is achieved by checking each character and, if it is an uppercase character, it is mapped to an + * underscore followed by a lowercase one. + * + * The reason there are two ternaries is that, for perfformance reasons, TS limits its + * character parsing to ~15 characters. + * To get around this we use the second turnery to parse 2 characters at a time, which allows us to support + * strings that are 30 characters long. + * + * If you get the TS #2589 error ("Type instantiation is excessively deep and possibly infinite") then most + * likely you have a string that's longer than 30 characters. + * Address this by reducing the length if possible, otherwise, you'll need to add a 3rd ternary which + * parses 3 chars at a time :grimace: + * + * For more details see this PR comment: https://github.com/microsoft/TypeScript/pull/40336#issuecomment-686723087 + */ +type CamelToSnake = string extends T + ? string + : T extends `${infer C0}${infer C1}${infer R}` + ? `${C0 extends Uppercase ? '_' : ''}${Lowercase}${C1 extends Uppercase + ? '_' + : ''}${Lowercase}${CamelToSnake}` + : T extends `${infer C0}${infer R}` + ? `${C0 extends Uppercase ? '_' : ''}${Lowercase}${CamelToSnake}` + : ''; diff --git a/x-pack/plugins/alerting/server/routes/lib/verify_access_and_context.ts b/x-pack/plugins/alerting/server/routes/lib/verify_access_and_context.ts new file mode 100644 index 0000000000000..f0177f04bf9b2 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/lib/verify_access_and_context.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RequestHandler } from 'kibana/server'; +import { ILicenseState, isErrorThatHandlesItsOwnResponse, verifyApiAccess } from '../../lib'; +import { AlertingRequestHandlerContext } from '../../types'; + +type AlertingRequestHandlerWrapper = ( + licenseState: ILicenseState, + handler: RequestHandler +) => RequestHandler; + +export const verifyAccessAndContext: AlertingRequestHandlerWrapper = (licenseState, handler) => { + return async (context, request, response) => { + verifyApiAccess(licenseState); + + if (!context.alerting) { + return response.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); + } + + try { + return await handler(context, request, response); + } catch (e) { + if (isErrorThatHandlesItsOwnResponse(e)) { + return e.sendResponse(response); + } + throw e; + } + }; +}; diff --git a/x-pack/plugins/alerting/server/routes/mute_alert.test.ts b/x-pack/plugins/alerting/server/routes/mute_alert.test.ts new file mode 100644 index 0000000000000..64ba22f2980ec --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_alert.test.ts @@ -0,0 +1,86 @@ +/* + * 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 { muteAlertRoute } from './mute_alert'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('muteAlertRoute', () => { + it('mutes an alert', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + muteAlertRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot( + `"/api/alerting/rule/{rule_id}/alert/{alert_id}/_mute"` + ); + + alertsClient.muteInstance.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + rule_id: '1', + alert_id: '2', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.muteInstance).toHaveBeenCalledTimes(1); + expect(alertsClient.muteInstance.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "alertId": "1", + "alertInstanceId": "2", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + muteAlertRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.muteInstance.mockRejectedValue( + new AlertTypeDisabledError('Fail', 'license_invalid') + ); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/mute_alert.ts b/x-pack/plugins/alerting/server/routes/mute_alert.ts new file mode 100644 index 0000000000000..f1b928cf8c543 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_alert.ts @@ -0,0 +1,55 @@ +/* + * 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 { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState, AlertTypeDisabledError } from '../lib'; +import { MuteOptions } from '../alerts_client'; +import { RewriteRequestCase, verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + rule_id: schema.string(), + alert_id: schema.string(), +}); + +const rewriteParamsReq: RewriteRequestCase = ({ + rule_id: alertId, + alert_id: alertInstanceId, +}) => ({ + alertId, + alertInstanceId, +}); + +export const muteAlertRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${BASE_ALERTING_API_PATH}/rule/{rule_id}/alert/{alert_id}/_mute`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const params = rewriteParamsReq(req.params); + try { + await alertsClient.muteInstance(params); + return res.noContent(); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts b/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts new file mode 100644 index 0000000000000..0d53708db2567 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_all_rule.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { muteAllRuleRoute } from './mute_all_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('muteAllRuleRoute', () => { + it('mute a rule', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + muteAllRuleRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}/_mute_all"`); + + alertsClient.muteAll.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.muteAll).toHaveBeenCalledTimes(1); + expect(alertsClient.muteAll.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + muteAllRuleRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.muteAll.mockRejectedValue(new AlertTypeDisabledError('Fail', 'license_invalid')); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/mute_all_rule.ts b/x-pack/plugins/alerting/server/routes/mute_all_rule.ts new file mode 100644 index 0000000000000..29d40249ef079 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/mute_all_rule.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState, AlertTypeDisabledError } from '../lib'; +import { verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const muteAllRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id}/_mute_all`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + try { + await alertsClient.muteAll({ id }); + return res.noContent(); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/rule_types.test.ts b/x-pack/plugins/alerting/server/routes/rule_types.test.ts new file mode 100644 index 0000000000000..58c9a4b4c46fd --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule_types.test.ts @@ -0,0 +1,223 @@ +/* + * 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 { ruleTypesRoute } from './rule_types'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { RecoveredActionGroup } from '../../common'; +import { RegistryAlertTypeWithAuth } from '../authorization'; +import { AsApiContract } from './lib'; + +const alertsClient = alertsClientMock.create(); + +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('ruleTypesRoute', () => { + it('lists rule types with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + ruleTypesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + authorizedConsumers: {}, + actionVariables: { + context: [], + state: [], + }, + producer: 'test', + enabledInLicense: true, + } as RegistryAlertTypeWithAuth, + ]; + const expectedResult: Array> = [ + { + id: '1', + name: 'name', + action_groups: [ + { + id: 'default', + name: 'Default', + }, + ], + default_action_group_id: 'default', + minimum_license_required: 'basic', + recovery_action_group: RecoveredActionGroup, + authorized_consumers: {}, + action_variables: { + context: [], + state: [], + }, + producer: 'test', + enabled_in_license: true, + }, + ]; + alertsClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes)); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [ + Object { + "action_groups": Array [ + Object { + "id": "default", + "name": "Default", + }, + ], + "action_variables": Object { + "context": Array [], + "state": Array [], + }, + "authorized_consumers": Object {}, + "default_action_group_id": "default", + "enabled_in_license": true, + "id": "1", + "minimum_license_required": "basic", + "name": "name", + "producer": "test", + "recovery_action_group": Object { + "id": "recovered", + "name": "Recovered", + }, + }, + ], + } + `); + + expect(alertsClient.listAlertTypes).toHaveBeenCalledTimes(1); + + expect(res.ok).toHaveBeenCalledWith({ + body: expectedResult, + }); + }); + + it('ensures the license allows listing rule types', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + ruleTypesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + authorizedConsumers: {}, + actionVariables: { + context: [], + state: [], + }, + producer: 'alerts', + enabledInLicense: true, + } as RegistryAlertTypeWithAuth, + ]; + + alertsClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes)); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents listing rule types', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + ruleTypesRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + recoveryActionGroup: RecoveredActionGroup, + authorizedConsumers: {}, + actionVariables: { + context: [], + state: [], + }, + producer: 'alerts', + enabledInLicense: true, + } as RegistryAlertTypeWithAuth, + ]; + + alertsClient.listAlertTypes.mockResolvedValueOnce(new Set(listTypes)); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/rule_types.ts b/x-pack/plugins/alerting/server/routes/rule_types.ts new file mode 100644 index 0000000000000..a3a44f9b013cd --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/rule_types.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { ILicenseState } from '../lib'; +import { RegistryAlertTypeWithAuth } from '../authorization'; +import { RewriteResponseCase, verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const rewriteBodyRes: RewriteResponseCase = (results) => { + return results.map( + ({ + enabledInLicense, + recoveryActionGroup, + actionGroups, + defaultActionGroupId, + minimumLicenseRequired, + actionVariables, + authorizedConsumers, + ...rest + }) => ({ + ...rest, + enabled_in_license: enabledInLicense, + recovery_action_group: recoveryActionGroup, + action_groups: actionGroups, + default_action_group_id: defaultActionGroupId, + minimum_license_required: minimumLicenseRequired, + action_variables: actionVariables, + authorized_consumers: authorizedConsumers, + }) + ); +}; + +export const ruleTypesRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${BASE_ALERTING_API_PATH}/rule_types`, + validate: {}, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const ruleTypes = Array.from(await context.alerting.getAlertsClient().listAlertTypes()); + return res.ok({ + body: rewriteBodyRes(ruleTypes), + }); + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/unmute_alert.test.ts b/x-pack/plugins/alerting/server/routes/unmute_alert.test.ts new file mode 100644 index 0000000000000..a491ba394f839 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_alert.test.ts @@ -0,0 +1,86 @@ +/* + * 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 { unmuteAlertRoute } from './unmute_alert'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('unmuteAlertRoute', () => { + it('unmutes an alert', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + unmuteAlertRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot( + `"/api/alerting/rule/{rule_id}/alert/{alert_id}/_unmute"` + ); + + alertsClient.unmuteInstance.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + rule_id: '1', + alert_id: '2', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.unmuteInstance).toHaveBeenCalledTimes(1); + expect(alertsClient.unmuteInstance.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "alertId": "1", + "alertInstanceId": "2", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + unmuteAlertRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.unmuteInstance.mockRejectedValue( + new AlertTypeDisabledError('Fail', 'license_invalid') + ); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/unmute_alert.ts b/x-pack/plugins/alerting/server/routes/unmute_alert.ts new file mode 100644 index 0000000000000..94bd6cd9af75f --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_alert.ts @@ -0,0 +1,55 @@ +/* + * 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 { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState, AlertTypeDisabledError } from '../lib'; +import { MuteOptions } from '../alerts_client'; +import { RewriteRequestCase, verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + rule_id: schema.string(), + alert_id: schema.string(), +}); + +const rewriteParamsReq: RewriteRequestCase = ({ + rule_id: alertId, + alert_id: alertInstanceId, +}) => ({ + alertId, + alertInstanceId, +}); + +export const unmuteAlertRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${BASE_ALERTING_API_PATH}/rule/{rule_id}/alert/{alert_id}/_unmute`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const params = rewriteParamsReq(req.params); + try { + await alertsClient.unmuteInstance(params); + return res.noContent(); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/unmute_all_rule.test.ts b/x-pack/plugins/alerting/server/routes/unmute_all_rule.test.ts new file mode 100644 index 0000000000000..f873863bcb902 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_all_rule.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { unmuteAllRuleRoute } from './unmute_all_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('unmuteAllRuleRoute', () => { + it('unmutes a rule', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + unmuteAllRuleRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}/_unmute_all"`); + + alertsClient.unmuteAll.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.unmuteAll).toHaveBeenCalledTimes(1); + expect(alertsClient.unmuteAll.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + unmuteAllRuleRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.unmuteAll.mockRejectedValue(new AlertTypeDisabledError('Fail', 'license_invalid')); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts b/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts new file mode 100644 index 0000000000000..96176e916cd7c --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/unmute_all_rule.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState, AlertTypeDisabledError } from '../lib'; +import { verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const unmuteAllRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id}/_unmute_all`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + try { + await alertsClient.unmuteAll({ id }); + return res.noContent(); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/update_rule.test.ts b/x-pack/plugins/alerting/server/routes/update_rule.test.ts new file mode 100644 index 0000000000000..a7121214cd3d3 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update_rule.test.ts @@ -0,0 +1,215 @@ +/* + * 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 { pick } from 'lodash'; +import { updateRuleRoute } from './update_rule'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib/license_api_access'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { UpdateOptions } from '../alerts_client'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; +import { AlertNotifyWhenType } from '../../common'; +import { AsApiContract } from './lib'; +import { PartialAlert } from '../types'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('updateRuleRoute', () => { + const mockedAlert = { + id: '1', + name: 'abc', + alertTypeId: '1', + tags: ['foo'], + throttle: '10m', + schedule: { interval: '12s' }, + params: { + otherField: false, + }, + createdAt: new Date(), + updatedAt: new Date(), + actions: [ + { + group: 'default', + id: '2', + actionTypeId: 'test', + params: { + baz: true, + }, + }, + ], + notifyWhen: 'onActionGroupChange' as AlertNotifyWhenType, + }; + + const updateRequest: AsApiContract['data']> = { + ...pick(mockedAlert, 'name', 'tags', 'schedule', 'params', 'throttle'), + notify_when: mockedAlert.notifyWhen, + actions: [ + { + group: mockedAlert.actions[0].group, + id: mockedAlert.actions[0].id, + params: mockedAlert.actions[0].params, + }, + ], + }; + + const updateResult: AsApiContract> = { + ...updateRequest, + id: mockedAlert.id, + updated_at: mockedAlert.updatedAt, + created_at: mockedAlert.createdAt, + rule_type_id: mockedAlert.alertTypeId, + actions: mockedAlert.actions.map(({ actionTypeId, ...rest }) => ({ + ...rest, + connector_type_id: actionTypeId, + })), + }; + + it('updates a rule with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + updateRuleRoute(router, licenseState); + + const [config, handler] = router.put.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/alerting/rule/{id}"`); + + alertsClient.update.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + body: updateRequest, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toEqual({ body: updateResult }); + + expect(alertsClient.update).toHaveBeenCalledTimes(1); + expect(alertsClient.update.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "data": Object { + "actions": Array [ + Object { + "group": "default", + "id": "2", + "params": Object { + "baz": true, + }, + }, + ], + "name": "abc", + "notifyWhen": "onActionGroupChange", + "params": Object { + "otherField": false, + }, + "schedule": Object { + "interval": "12s", + }, + "tags": Array [ + "foo", + ], + "throttle": "10m", + }, + "id": "1", + }, + ] + `); + + expect(res.ok).toHaveBeenCalled(); + }); + + it('ensures the license allows updating rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + updateRuleRoute(router, licenseState); + + const [, handler] = router.put.mock.calls[0]; + + alertsClient.update.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + body: updateRequest, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents updating rules', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + updateRuleRoute(router, licenseState); + + const [, handler] = router.put.mock.calls[0]; + + alertsClient.update.mockResolvedValueOnce(mockedAlert); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + body: updateRequest, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + updateRuleRoute(router, licenseState); + + const [, handler] = router.put.mock.calls[0]; + + alertsClient.update.mockRejectedValue(new AlertTypeDisabledError('Fail', 'license_invalid')); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/update_rule.ts b/x-pack/plugins/alerting/server/routes/update_rule.ts new file mode 100644 index 0000000000000..ef5bd00558752 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update_rule.ts @@ -0,0 +1,147 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; +import { ILicenseState, AlertTypeDisabledError, validateDurationSchema } from '../lib'; +import { AlertNotifyWhenType } from '../../common'; +import { UpdateOptions } from '../alerts_client'; +import { + verifyAccessAndContext, + RewriteResponseCase, + RewriteRequestCase, + handleDisabledApiKeysError, +} from './lib'; +import { + AlertTypeParams, + AlertingRequestHandlerContext, + BASE_ALERTING_API_PATH, + validateNotifyWhenType, + PartialAlert, +} from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +const bodySchema = schema.object({ + name: schema.string(), + tags: schema.arrayOf(schema.string(), { defaultValue: [] }), + schedule: schema.object({ + interval: schema.string({ validate: validateDurationSchema }), + }), + throttle: schema.nullable(schema.string({ validate: validateDurationSchema })), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + actions: schema.arrayOf( + schema.object({ + group: schema.string(), + id: schema.string(), + params: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + }), + { defaultValue: [] } + ), + notify_when: schema.string({ validate: validateNotifyWhenType }), +}); + +const rewriteBodyReq: RewriteRequestCase> = (result) => { + const { notify_when: notifyWhen, ...rest } = result.data; + return { + ...result, + data: { + ...rest, + notifyWhen, + }, + }; +}; +const rewriteBodyRes: RewriteResponseCase> = ({ + actions, + alertTypeId, + scheduledTaskId, + createdBy, + updatedBy, + createdAt, + updatedAt, + apiKeyOwner, + notifyWhen, + muteAll, + mutedInstanceIds, + executionStatus, + ...rest +}) => ({ + ...rest, + api_key_owner: apiKeyOwner, + created_by: createdBy, + updated_by: updatedBy, + ...(alertTypeId ? { rule_type_id: alertTypeId } : {}), + ...(scheduledTaskId ? { scheduled_task_id: scheduledTaskId } : {}), + ...(createdAt ? { created_at: createdAt } : {}), + ...(updatedAt ? { updated_at: updatedAt } : {}), + ...(notifyWhen ? { notify_when: notifyWhen } : {}), + ...(muteAll !== undefined ? { mute_all: muteAll } : {}), + ...(mutedInstanceIds ? { muted_alert_ids: mutedInstanceIds } : {}), + ...(executionStatus + ? { + execution_status: { + status: executionStatus.status, + last_execution_date: executionStatus.lastExecutionDate, + }, + } + : {}), + ...(actions + ? { + actions: actions.map(({ group, id, actionTypeId, params }) => ({ + group, + id, + params, + connector_type_id: actionTypeId, + })), + } + : {}), +}); + +export const updateRuleRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.put( + { + path: `${BASE_ALERTING_API_PATH}/rule/{id}`, + validate: { + body: bodySchema, + params: paramSchema, + }, + }, + handleDisabledApiKeysError( + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + const rule = req.body; + try { + const alertRes = await alertsClient.update( + rewriteBodyReq({ + id, + data: { + ...rule, + notify_when: rule.notify_when as AlertNotifyWhenType, + }, + }) + ); + return res.ok({ + body: rewriteBodyRes(alertRes), + }); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ) + ); +}; diff --git a/x-pack/plugins/alerting/server/routes/update_rule_api_key.test.ts b/x-pack/plugins/alerting/server/routes/update_rule_api_key.test.ts new file mode 100644 index 0000000000000..ced335136adb1 --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update_rule_api_key.test.ts @@ -0,0 +1,82 @@ +/* + * 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 { updateRuleApiKeyRoute } from './update_rule_api_key'; +import { httpServiceMock } from 'src/core/server/mocks'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { mockHandlerArguments } from './_mock_handler_arguments'; +import { alertsClientMock } from '../alerts_client.mock'; +import { AlertTypeDisabledError } from '../lib/errors/alert_type_disabled'; + +const alertsClient = alertsClientMock.create(); +jest.mock('../lib/license_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('updateRuleApiKeyRoute', () => { + it('updates api key for a rule', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + updateRuleApiKeyRoute(router, licenseState); + + const [config, handler] = router.post.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/internal/alerting/rule/{id}/_update_api_key"`); + + alertsClient.updateApiKey.mockResolvedValueOnce(); + + const [context, req, res] = mockHandlerArguments( + { alertsClient }, + { + params: { + id: '1', + }, + }, + ['noContent'] + ); + + expect(await handler(context, req, res)).toEqual(undefined); + + expect(alertsClient.updateApiKey).toHaveBeenCalledTimes(1); + expect(alertsClient.updateApiKey.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "id": "1", + }, + ] + `); + + expect(res.noContent).toHaveBeenCalled(); + }); + + it('ensures the rule type gets validated for the license', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + updateRuleApiKeyRoute(router, licenseState); + + const [, handler] = router.post.mock.calls[0]; + + alertsClient.updateApiKey.mockRejectedValue( + new AlertTypeDisabledError('Fail', 'license_invalid') + ); + + const [context, req, res] = mockHandlerArguments({ alertsClient }, { params: {}, body: {} }, [ + 'ok', + 'forbidden', + ]); + + await handler(context, req, res); + + expect(res.forbidden).toHaveBeenCalledWith({ body: { message: 'Fail' } }); + }); +}); diff --git a/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts b/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts new file mode 100644 index 0000000000000..57206c68d448d --- /dev/null +++ b/x-pack/plugins/alerting/server/routes/update_rule_api_key.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IRouter } from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { ILicenseState, AlertTypeDisabledError } from '../lib'; +import { verifyAccessAndContext } from './lib'; +import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types'; + +const paramSchema = schema.object({ + id: schema.string(), +}); + +export const updateRuleApiKeyRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.post( + { + path: `${INTERNAL_BASE_ALERTING_API_PATH}/rule/{id}/_update_api_key`, + validate: { + params: paramSchema, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const alertsClient = context.alerting.getAlertsClient(); + const { id } = req.params; + try { + await alertsClient.updateApiKey({ id }); + return res.noContent(); + } catch (e) { + if (e instanceof AlertTypeDisabledError) { + return e.sendResponse(res); + } + throw e; + } + }) + ) + ); +}; diff --git a/x-pack/plugins/data_enhanced/public/search/search_abort_controller.test.ts b/x-pack/plugins/data_enhanced/public/search/search_abort_controller.test.ts new file mode 100644 index 0000000000000..68282c1e947f7 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/search_abort_controller.test.ts @@ -0,0 +1,105 @@ +/* + * 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 { SearchAbortController } from './search_abort_controller'; + +const timeTravel = (msToRun = 0) => { + jest.advanceTimersByTime(msToRun); + return new Promise((resolve) => setImmediate(resolve)); +}; + +describe('search abort controller', () => { + test('is not aborted when empty', () => { + const sac = new SearchAbortController(); + expect(sac.getSignal().aborted).toBe(false); + }); + + test('immediately aborts when passed an aborted signal in the constructor', () => { + const controller = new AbortController(); + controller.abort(); + const sac = new SearchAbortController(controller.signal); + expect(sac.getSignal().aborted).toBe(true); + }); + + test('aborts when input signal is aborted', () => { + const controller = new AbortController(); + const sac = new SearchAbortController(controller.signal); + expect(sac.getSignal().aborted).toBe(false); + controller.abort(); + expect(sac.getSignal().aborted).toBe(true); + }); + + test('aborts when all input signals are aborted', () => { + const controller = new AbortController(); + const sac = new SearchAbortController(controller.signal); + + const controller2 = new AbortController(); + sac.addAbortSignal(controller2.signal); + expect(sac.getSignal().aborted).toBe(false); + controller.abort(); + expect(sac.getSignal().aborted).toBe(false); + controller2.abort(); + expect(sac.getSignal().aborted).toBe(true); + }); + + test('aborts explicitly even if all inputs are not aborted', () => { + const controller = new AbortController(); + const sac = new SearchAbortController(controller.signal); + + const controller2 = new AbortController(); + sac.addAbortSignal(controller2.signal); + + expect(sac.getSignal().aborted).toBe(false); + sac.abort(); + expect(sac.getSignal().aborted).toBe(true); + }); + + test('doesnt abort, if cleared', () => { + const controller = new AbortController(); + const sac = new SearchAbortController(controller.signal); + expect(sac.getSignal().aborted).toBe(false); + sac.cleanup(); + controller.abort(); + expect(sac.getSignal().aborted).toBe(false); + }); + + describe('timeout abort', () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + test('doesnt abort on timeout, if cleared', () => { + const sac = new SearchAbortController(undefined, 100); + expect(sac.getSignal().aborted).toBe(false); + sac.cleanup(); + timeTravel(100); + expect(sac.getSignal().aborted).toBe(false); + }); + + test('aborts on timeout, even if no signals passed in', () => { + const sac = new SearchAbortController(undefined, 100); + expect(sac.getSignal().aborted).toBe(false); + timeTravel(100); + expect(sac.getSignal().aborted).toBe(true); + expect(sac.isTimeout()).toBe(true); + }); + + test('aborts on timeout, even if there are unaborted signals', () => { + const controller = new AbortController(); + const sac = new SearchAbortController(controller.signal, 100); + + expect(sac.getSignal().aborted).toBe(false); + timeTravel(100); + expect(sac.getSignal().aborted).toBe(true); + expect(sac.isTimeout()).toBe(true); + }); + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/search_abort_controller.ts b/x-pack/plugins/data_enhanced/public/search/search_abort_controller.ts new file mode 100644 index 0000000000000..4482a7771dc28 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/search_abort_controller.ts @@ -0,0 +1,78 @@ +/* + * 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 { Subscription, timer } from 'rxjs'; + +export enum AbortReason { + Timeout = 'timeout', +} + +export class SearchAbortController { + private inputAbortSignals: AbortSignal[] = new Array(); + private abortController: AbortController = new AbortController(); + private timeoutSub?: Subscription; + private destroyed = false; + private reason?: AbortReason; + + constructor(abortSignal?: AbortSignal, timeout?: number) { + if (abortSignal) { + this.addAbortSignal(abortSignal); + } + + if (timeout) { + this.timeoutSub = timer(timeout).subscribe(() => { + this.reason = AbortReason.Timeout; + this.abortController.abort(); + this.timeoutSub!.unsubscribe(); + }); + } + } + + private abortHandler = () => { + const allAborted = this.inputAbortSignals.every((signal) => signal.aborted); + if (allAborted) { + this.abortController.abort(); + this.cleanup(); + } + }; + + public cleanup() { + this.destroyed = true; + this.timeoutSub?.unsubscribe(); + this.inputAbortSignals.forEach((abortSignal) => { + abortSignal.removeEventListener('abort', this.abortHandler); + }); + } + + public addAbortSignal(inputSignal: AbortSignal) { + if (this.destroyed) { + return; + } + + this.inputAbortSignals.push(inputSignal); + + if (inputSignal.aborted) { + this.abortHandler(); + } else { + // abort our internal controller if the input signal aborts + inputSignal.addEventListener('abort', this.abortHandler); + } + } + + public getSignal() { + return this.abortController.signal; + } + + public abort() { + this.cleanup(); + this.abortController.abort(); + } + + public isTimeout() { + return this.reason === AbortReason.Timeout; + } +} diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index 0dfec1a35d900..b9d8553d3dc5a 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -17,6 +17,7 @@ import { SearchSessionState, } from '../../../../../src/plugins/data/public'; import { ENHANCED_ES_SEARCH_STRATEGY, IAsyncSearchOptions, pollSearch } from '../../common'; +import { SearchAbortController } from './search_abort_controller'; export class EnhancedSearchInterceptor extends SearchInterceptor { private uiSettingsSub: Subscription; @@ -47,31 +48,30 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { } public search({ id, ...request }: IKibanaSearchRequest, options: IAsyncSearchOptions = {}) { - const { combinedSignal, timeoutSignal, cleanup, abort } = this.setupAbortSignal({ - abortSignal: options.abortSignal, - timeout: this.searchTimeout, - }); - const strategy = options?.strategy ?? ENHANCED_ES_SEARCH_STRATEGY; - const searchOptions = { ...options, strategy, abortSignal: combinedSignal }; + const searchOptions = { + strategy: ENHANCED_ES_SEARCH_STRATEGY, + ...options, + }; + const { sessionId, strategy, abortSignal } = searchOptions; const search = () => this.runSearch({ id, ...request }, searchOptions); + const searchAbortController = new SearchAbortController(abortSignal, this.searchTimeout); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - - const untrackSearch = - this.deps.session.isCurrentSession(options.sessionId) && - this.deps.session.trackSearch({ abort }); + const untrackSearch = this.deps.session.isCurrentSession(options.sessionId) + ? this.deps.session.trackSearch({ abort: () => searchAbortController.abort() }) + : undefined; // track if this search's session will be send to background // if yes, then we don't need to cancel this search when it is aborted let isSavedToBackground = false; const savedToBackgroundSub = - this.deps.session.isCurrentSession(options.sessionId) && + this.deps.session.isCurrentSession(sessionId) && this.deps.session.state$ .pipe( skip(1), // ignore any state, we are only interested in transition x -> BackgroundLoading filter( (state) => - this.deps.session.isCurrentSession(options.sessionId) && + this.deps.session.isCurrentSession(sessionId) && state === SearchSessionState.BackgroundLoading ), take(1) @@ -84,15 +84,18 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { if (id && !isSavedToBackground) this.deps.http.delete(`/internal/search/${strategy}/${id}`); }); - return pollSearch(search, cancel, { ...options, abortSignal: combinedSignal }).pipe( + return pollSearch(search, cancel, { + ...options, + abortSignal: searchAbortController.getSignal(), + }).pipe( tap((response) => (id = response.id)), catchError((e: Error) => { cancel(); - return throwError(this.handleSearchError(e, timeoutSignal, options)); + return throwError(this.handleSearchError(e, options, searchAbortController.isTimeout())); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); - cleanup(); + searchAbortController.cleanup(); if (untrackSearch && this.deps.session.isCurrentSession(options.sessionId)) { // untrack if this search still belongs to current session untrackSearch(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.test.tsx index a737174477177..222041c9f85a3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.test.tsx @@ -14,27 +14,37 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiEmptyPrompt } from '@elastic/eui'; +import { SampleEngineCreationCta } from '../../sample_engine_creation_cta'; + import { EmptyState } from './'; describe('EmptyState', () => { describe('when the user can manage/create engines', () => { let wrapper: ShallowWrapper; + let prompt: ShallowWrapper; beforeAll(() => { setMockValues({ myRole: { canManageEngines: true } }); wrapper = shallow(); + prompt = wrapper.find(EuiEmptyPrompt).dive(); + }); + + afterAll(() => { + jest.clearAllMocks(); }); it('renders a prompt to create an engine', () => { expect(wrapper.find('[data-test-subj="AdminEmptyEnginesPrompt"]')).toHaveLength(1); }); + it('contains a sample engine CTA', () => { + expect(prompt.find(SampleEngineCreationCta)).toHaveLength(1); + }); + describe('create engine button', () => { - let prompt: ShallowWrapper; let button: ShallowWrapper; beforeAll(() => { - prompt = wrapper.find(EuiEmptyPrompt).dive(); button = prompt.find('[data-test-subj="EmptyStateCreateFirstEngineCta"]'); }); @@ -50,13 +60,18 @@ describe('EmptyState', () => { }); describe('when the user cannot manage/create engines', () => { + let wrapper: ShallowWrapper; + beforeAll(() => { setMockValues({ myRole: { canManageEngines: false } }); + wrapper = shallow(); }); - it('renders a prompt to contact the App Search admin', () => { - const wrapper = shallow(); + afterAll(() => { + jest.clearAllMocks(); + }); + it('renders a prompt to contact the App Search admin', () => { expect(wrapper.find('[data-test-subj="NonAdminEmptyEnginesPrompt"]')).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx index df5a057e5d9c6..37c67c1f8d6f2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/components/empty_state.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { useValues, useActions } from 'kea'; -import { EuiPageContent, EuiEmptyPrompt } from '@elastic/eui'; +import { EuiPageContent, EuiEmptyPrompt, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; @@ -18,6 +18,8 @@ import { TelemetryLogic } from '../../../../shared/telemetry'; import { AppLogic } from '../../../app_logic'; import { ENGINE_CREATION_PATH } from '../../../routes'; +import { SampleEngineCreationCta } from '../../sample_engine_creation_cta/sample_engine_creation_cta'; + import { EnginesOverviewHeader } from './header'; import './empty_state.scss'; @@ -55,22 +57,26 @@ export const EmptyState: React.FC = () => {

    } actions={ - - sendAppSearchTelemetry({ - action: 'clicked', - metric: 'create_first_engine_button', - }) - } - > - {i18n.translate( - 'xpack.enterpriseSearch.appSearch.emptyState.createFirstEngineCta', - { defaultMessage: 'Create an engine' } - )} - + <> + + sendAppSearchTelemetry({ + action: 'clicked', + metric: 'create_first_engine_button', + }) + } + > + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.emptyState.createFirstEngineCta', + { defaultMessage: 'Create an engine' } + )} + + + + } /> ) : ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/i18n.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/i18n.ts new file mode 100644 index 0000000000000..229a26b0fb360 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/i18n.ts @@ -0,0 +1,29 @@ +/* + * 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'; + +export const SAMPLE_ENGINE_CREATION_CTA_TITLE = i18n.translate( + 'xpack.enterpriseSearch.appSearch.sampleEngineCreationCta.title', + { + defaultMessage: 'Just kicking the tires?', + } +); + +export const SAMPLE_ENGINE_CREATION_CTA_DESCRIPTION = i18n.translate( + 'xpack.enterpriseSearch.appSearch.sampleEngineCreationCta.description', + { + defaultMessage: 'Test an engine with sample data.', + } +); + +export const SAMPLE_ENGINE_CREATION_CTA_BUTTON_LABEL = i18n.translate( + 'xpack.enterpriseSearch.appSearch.sampleEngineCreationCta.buttonLabel', + { + defaultMessage: 'Try a sample engine', + } +); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/index.ts new file mode 100644 index 0000000000000..fa11abd19a2db --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { SampleEngineCreationCta } from './sample_engine_creation_cta'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta.test.tsx new file mode 100644 index 0000000000000..992afe4356a07 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta.test.tsx @@ -0,0 +1,57 @@ +/* + * 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 '../../../__mocks__/enterprise_search_url.mock'; +import { setMockActions, setMockValues } from '../../../__mocks__'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EuiButton } from '@elastic/eui'; + +import { SampleEngineCreationCta } from './sample_engine_creation_cta'; + +describe('SampleEngineCTA', () => { + describe('CTA button', () => { + const MOCK_VALUES = { + isLoading: false, + }; + + const MOCK_ACTIONS = { + createSampleEngine: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + setMockActions(MOCK_ACTIONS); + setMockValues(MOCK_VALUES); + }); + + it('calls createSampleEngine on click', () => { + const wrapper = shallow(); + const ctaButton = wrapper.find(EuiButton); + + expect(ctaButton.props().onClick).toEqual(MOCK_ACTIONS.createSampleEngine); + }); + + it('is enabled by default', () => { + const wrapper = shallow(); + const ctaButton = wrapper.find(EuiButton); + + expect(ctaButton.props().isLoading).toEqual(false); + }); + + it('is disabled while loading', () => { + setMockValues({ ...MOCK_VALUES, isLoading: true }); + const wrapper = shallow(); + const ctaButton = wrapper.find(EuiButton); + + expect(ctaButton.props().isLoading).toEqual(true); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta.tsx new file mode 100644 index 0000000000000..8de6b6030ef66 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useActions, useValues } from 'kea'; + +import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiText, EuiButton } from '@elastic/eui'; + +import { + SAMPLE_ENGINE_CREATION_CTA_TITLE, + SAMPLE_ENGINE_CREATION_CTA_DESCRIPTION, + SAMPLE_ENGINE_CREATION_CTA_BUTTON_LABEL, +} from './i18n'; +import { SampleEngineCreationCtaLogic } from './sample_engine_creation_cta_logic'; + +export const SampleEngineCreationCta: React.FC = () => { + const { isLoading } = useValues(SampleEngineCreationCtaLogic); + const { createSampleEngine } = useActions(SampleEngineCreationCtaLogic); + + return ( + + + + +

    {SAMPLE_ENGINE_CREATION_CTA_TITLE}

    +
    + +

    {SAMPLE_ENGINE_CREATION_CTA_DESCRIPTION}

    +
    +
    + + + {SAMPLE_ENGINE_CREATION_CTA_BUTTON_LABEL} + + +
    +
    + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta_logic.test.ts new file mode 100644 index 0000000000000..740c4df697d68 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta_logic.test.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 { + LogicMounter, + mockHttpValues, + mockKibanaValues, + mockFlashMessageHelpers, +} from '../../../__mocks__'; + +import { nextTick } from '@kbn/test/jest'; + +import { SampleEngineCreationCtaLogic } from './sample_engine_creation_cta_logic'; + +describe('SampleEngineCreationCtaLogic', () => { + const { mount } = new LogicMounter(SampleEngineCreationCtaLogic); + const { http } = mockHttpValues; + const { navigateToUrl } = mockKibanaValues; + const { setQueuedSuccessMessage, flashAPIErrors } = mockFlashMessageHelpers; + + const DEFAULT_VALUES = { + isLoading: false, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mount(); + }); + + it('has expected default values', () => { + expect(SampleEngineCreationCtaLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('actions', () => { + it('onSampleEngineCreationFailure sets isLoading to false', () => { + mount({ isLoading: true }); + + SampleEngineCreationCtaLogic.actions.onSampleEngineCreationFailure(); + + expect(SampleEngineCreationCtaLogic.values.isLoading).toEqual(false); + }); + }); + + describe('listeners', () => { + describe('createSampleEngine', () => { + it('POSTS to /api/app_search/engines', () => { + const body = JSON.stringify({ + seed_sample_engine: true, + }); + SampleEngineCreationCtaLogic.actions.createSampleEngine(); + + expect(http.post).toHaveBeenCalledWith('/api/app_search/onboarding_complete', { body }); + }); + + it('calls onSampleEngineCreationSuccess on valid submission', async () => { + jest.spyOn(SampleEngineCreationCtaLogic.actions, 'onSampleEngineCreationSuccess'); + http.post.mockReturnValueOnce(Promise.resolve({})); + + SampleEngineCreationCtaLogic.actions.createSampleEngine(); + await nextTick(); + + expect( + SampleEngineCreationCtaLogic.actions.onSampleEngineCreationSuccess + ).toHaveBeenCalledTimes(1); + }); + + it('calls onSampleEngineCreationFailure and flashAPIErrors on API Error', async () => { + jest.spyOn(SampleEngineCreationCtaLogic.actions, 'onSampleEngineCreationFailure'); + http.post.mockReturnValueOnce(Promise.reject()); + + SampleEngineCreationCtaLogic.actions.createSampleEngine(); + await nextTick(); + + expect(flashAPIErrors).toHaveBeenCalledTimes(1); + expect( + SampleEngineCreationCtaLogic.actions.onSampleEngineCreationFailure + ).toHaveBeenCalledTimes(1); + }); + }); + + it('onSampleEngineCreationSuccess should set a success message and navigate the user to the engine page', () => { + SampleEngineCreationCtaLogic.actions.onSampleEngineCreationSuccess(); + + expect(setQueuedSuccessMessage).toHaveBeenCalledWith('Successfully created engine.'); + expect(navigateToUrl).toHaveBeenCalledWith('/engines/national-parks-demo'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta_logic.ts new file mode 100644 index 0000000000000..37570d4e3cfe7 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/sample_engine_creation_cta/sample_engine_creation_cta_logic.ts @@ -0,0 +1,72 @@ +/* + * 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 { generatePath } from 'react-router-dom'; + +import { kea, MakeLogicType } from 'kea'; + +import { flashAPIErrors, setQueuedSuccessMessage } from '../../../shared/flash_messages'; +import { HttpLogic } from '../../../shared/http'; +import { KibanaLogic } from '../../../shared/kibana'; +import { ENGINE_PATH } from '../../routes'; +import { ENGINE_CREATION_SUCCESS_MESSAGE } from '../engine_creation/constants'; + +interface SampleEngineCreationCtaActions { + createSampleEngine(): void; + onSampleEngineCreationSuccess(): void; + onSampleEngineCreationFailure(): void; + setIsLoading(isLoading: boolean): { isLoading: boolean }; +} + +interface SampleEngineCreationCtaValues { + isLoading: boolean; +} + +export const SampleEngineCreationCtaLogic = kea< + MakeLogicType +>({ + path: ['enterprise_search', 'app_search', 'sample_engine_cta_logic'], + actions: { + createSampleEngine: true, + onSampleEngineCreationSuccess: true, + onSampleEngineCreationFailure: true, + }, + reducers: { + isLoading: [ + false, + { + createSampleEngine: () => true, + onSampleEngineCreationSuccess: () => false, + onSampleEngineCreationFailure: () => false, + }, + ], + }, + listeners: ({ actions }) => ({ + createSampleEngine: async () => { + const { http } = HttpLogic.values; + + const body = JSON.stringify({ seed_sample_engine: true }); + + try { + await http.post('/api/app_search/onboarding_complete', { + body, + }); + actions.onSampleEngineCreationSuccess(); + } catch (e) { + actions.onSampleEngineCreationFailure(); + flashAPIErrors(e); + } + }, + onSampleEngineCreationSuccess: () => { + const { navigateToUrl } = KibanaLogic.values; + const enginePath = generatePath(ENGINE_PATH, { engineName: 'national-parks-demo' }); + + setQueuedSuccessMessage(ENGINE_CREATION_SUCCESS_MESSAGE); + navigateToUrl(enginePath); + }, + }), +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx index f13189afe8252..c6cefba317cce 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx @@ -106,4 +106,26 @@ describe('SourceSettings', () => { sourceConfigData.configuredFields.publicKey ); }); + + describe('DownloadDiagnosticsButton', () => { + it('renders for org with correct href', () => { + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DownloadDiagnosticsButton"]').prop('href')).toEqual( + '/api/workplace_search/org/sources/123/download_diagnostics' + ); + }); + + it('renders for account with correct href', () => { + setMockValues({ + ...mockValues, + isOrganization: false, + }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DownloadDiagnosticsButton"]').prop('href')).toEqual( + '/api/workplace_search/account/sources/123/download_diagnostics' + ); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx index d99f9a4cb1a46..7ba53822534cf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx @@ -44,6 +44,9 @@ import { SOURCE_CONFIG_LINK, SOURCE_REMOVE_TITLE, SOURCE_REMOVE_DESCRIPTION, + SYNC_DIAGNOSTICS_TITLE, + SYNC_DIAGNOSTICS_DESCRIPTION, + SYNC_DIAGNOSTICS_BUTTON, } from '../constants'; import { staticSourceData } from '../source_data'; import { SourceLogic } from '../source_logic'; @@ -82,6 +85,10 @@ export const SourceSettings: React.FC = () => { const { clientId, clientSecret, publicKey, consumerKey, baseUrl } = configuredFields || {}; + const diagnosticsPath = isOrganization + ? `/api/workplace_search/org/sources/${id}/download_diagnostics` + : `/api/workplace_search/account/sources/${id}/download_diagnostics`; + const handleNameChange = (e: ChangeEvent) => setValue(e.target.value); const submitNameChange = (e: FormEvent) => { @@ -167,6 +174,17 @@ export const SourceSettings: React.FC = () => { )} + + + {SYNC_DIAGNOSTICS_BUTTON} + + { }); }); }); + + it('works if response contains no json data', async () => { + EnterpriseSearchAPI.mockReturn(); + + const requestHandler = enterpriseSearchRequestHandler.createRequest({ path: '/api/prep' }); + await makeAPICall(requestHandler); + + expect(responseMock.custom).toHaveBeenCalledWith({ + statusCode: 200, + headers: mockExpectedResponseHeaders, + }); + }); }); describe('error responses', () => { @@ -456,10 +468,12 @@ const EnterpriseSearchAPI = { ...expectedParams, }); }, - mockReturn(response: object, options?: any) { + mockReturn(response?: object, options?: any) { fetchMock.mockImplementation(() => { const headers = Object.assign({}, mockExpectedResponseHeaders, options?.headers); - return Promise.resolve(new Response(JSON.stringify(response), { ...options, headers })); + return Promise.resolve( + new Response(response ? JSON.stringify(response) : undefined, { ...options, headers }) + ); }); }, mockReturnError() { diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts index fb525740dd55b..e5394fd580efc 100644 --- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts +++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts @@ -113,22 +113,32 @@ export class EnterpriseSearchRequestHandler { } // Check returned data - const json = await apiResponse.json(); - if (!hasValidData(json)) { - return this.handleInvalidDataError(response, url, json); - } + let responseBody; - // Intercept data that is meant for the server side session - const { _sessionData, ...responseJson } = json; - if (_sessionData) { - this.setSessionData(_sessionData); + try { + const json = await apiResponse.json(); + + if (!hasValidData(json)) { + return this.handleInvalidDataError(response, url, json); + } + + // Intercept data that is meant for the server side session + const { _sessionData, ...responseJson } = json; + if (_sessionData) { + this.setSessionData(_sessionData); + responseBody = responseJson; + } else { + responseBody = json; + } + } catch (e) { + responseBody = undefined; } // Pass successful responses back to the front-end return response.custom({ statusCode: status, headers: this.headers, - body: _sessionData ? responseJson : json, + body: responseBody, }); } catch (e) { // Catch connection/auth errors diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts index 1bd88c111f79f..3c8501ec15b3d 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/index.ts @@ -12,6 +12,7 @@ import { registerCredentialsRoutes } from './credentials'; import { registerCurationsRoutes } from './curations'; import { registerDocumentsRoutes, registerDocumentRoutes } from './documents'; import { registerEnginesRoutes } from './engines'; +import { registerOnboardingRoutes } from './onboarding'; import { registerResultSettingsRoutes } from './result_settings'; import { registerRoleMappingsRoutes } from './role_mappings'; import { registerSearchSettingsRoutes } from './search_settings'; @@ -28,4 +29,5 @@ export const registerAppSearchRoutes = (dependencies: RouteDependencies) => { registerSearchSettingsRoutes(dependencies); registerRoleMappingsRoutes(dependencies); registerResultSettingsRoutes(dependencies); + registerOnboardingRoutes(dependencies); }; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/onboarding.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/onboarding.test.ts new file mode 100644 index 0000000000000..c26f8dbaf5213 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/onboarding.test.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MockRouter, mockRequestHandler, mockDependencies } from '../../__mocks__'; + +import { registerOnboardingRoutes } from './onboarding'; + +describe('engine routes', () => { + describe('POST /api/app_search/onboarding_complete', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'post', + path: '/api/app_search/onboarding_complete', + }); + + registerOnboardingRoutes({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + mockRouter.callRoute({ body: {} }); + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/as/onboarding/complete', + }); + }); + + it('validates seed_sample_engine ', () => { + const request = { body: { seed_sample_engine: true } }; + mockRouter.shouldValidate(request); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/onboarding.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/onboarding.ts new file mode 100644 index 0000000000000..9a46c75555969 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/onboarding.ts @@ -0,0 +1,29 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +import { RouteDependencies } from '../../plugin'; + +export function registerOnboardingRoutes({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.post( + { + path: '/api/app_search/onboarding_complete', + validate: { + body: schema.object({ + seed_sample_engine: schema.maybe(schema.boolean()), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/as/onboarding/complete', + }) + ); +} diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts index 1443a78854abf..2fc4099a78615 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts @@ -25,6 +25,7 @@ import { registerAccountSourceSchemasRoute, registerAccountSourceReindexJobRoute, registerAccountSourceReindexJobStatusRoute, + registerAccountSourceDownloadDiagnosticsRoute, registerOrgSourcesRoute, registerOrgSourcesStatusRoute, registerOrgSourceRoute, @@ -40,6 +41,7 @@ import { registerOrgSourceSchemasRoute, registerOrgSourceReindexJobRoute, registerOrgSourceReindexJobStatusRoute, + registerOrgSourceDownloadDiagnosticsRoute, registerOrgSourceOauthConfigurationsRoute, registerOrgSourceOauthConfigurationRoute, registerOauthConnectorParamsRoute, @@ -563,6 +565,29 @@ describe('sources routes', () => { }); }); + describe('GET /api/workplace_search/account/sources/{sourceId}/download_diagnostics', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/workplace_search/account/sources/{sourceId}/download_diagnostics', + }); + + registerAccountSourceDownloadDiagnosticsRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/ws/sources/:sourceId/download_diagnostics', + }); + }); + }); + describe('GET /api/workplace_search/org/sources', () => { let mockRouter: MockRouter; @@ -1061,6 +1086,29 @@ describe('sources routes', () => { }); }); + describe('GET /api/workplace_search/org/sources/{sourceId}/download_diagnostics', () => { + let mockRouter: MockRouter; + + beforeEach(() => { + jest.clearAllMocks(); + mockRouter = new MockRouter({ + method: 'get', + path: '/api/workplace_search/org/sources/{sourceId}/download_diagnostics', + }); + + registerOrgSourceDownloadDiagnosticsRoute({ + ...mockDependencies, + router: mockRouter.router, + }); + }); + + it('creates a request handler', () => { + expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ + path: '/ws/org/sources/:sourceId/download_diagnostics', + }); + }); + }); + describe('GET /api/workplace_search/org/settings/connectors', () => { let mockRouter: MockRouter; diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts index d50ff6564b02e..8257dd0dc52b0 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts @@ -401,6 +401,25 @@ export function registerAccountSourceReindexJobStatusRoute({ ); } +export function registerAccountSourceDownloadDiagnosticsRoute({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.get( + { + path: '/api/workplace_search/account/sources/{sourceId}/download_diagnostics', + validate: { + params: schema.object({ + sourceId: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:sourceId/download_diagnostics', + }) + ); +} + // Org routes export function registerOrgSourcesRoute({ router, @@ -750,6 +769,25 @@ export function registerOrgSourceReindexJobStatusRoute({ ); } +export function registerOrgSourceDownloadDiagnosticsRoute({ + router, + enterpriseSearchRequestHandler, +}: RouteDependencies) { + router.get( + { + path: '/api/workplace_search/org/sources/{sourceId}/download_diagnostics', + validate: { + params: schema.object({ + sourceId: schema.string(), + }), + }, + }, + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:sourceId/download_diagnostics', + }) + ); +} + export function registerOrgSourceOauthConfigurationsRoute({ router, enterpriseSearchRequestHandler, @@ -900,6 +938,7 @@ export const registerSourcesRoutes = (dependencies: RouteDependencies) => { registerAccountSourceSchemasRoute(dependencies); registerAccountSourceReindexJobRoute(dependencies); registerAccountSourceReindexJobStatusRoute(dependencies); + registerAccountSourceDownloadDiagnosticsRoute(dependencies); registerOrgSourcesRoute(dependencies); registerOrgSourcesStatusRoute(dependencies); registerOrgSourceRoute(dependencies); @@ -915,6 +954,7 @@ export const registerSourcesRoutes = (dependencies: RouteDependencies) => { registerOrgSourceSchemasRoute(dependencies); registerOrgSourceReindexJobRoute(dependencies); registerOrgSourceReindexJobStatusRoute(dependencies); + registerOrgSourceDownloadDiagnosticsRoute(dependencies); registerOrgSourceOauthConfigurationsRoute(dependencies); registerOrgSourceOauthConfigurationRoute(dependencies); registerOauthConnectorParamsRoute(dependencies); diff --git a/x-pack/plugins/file_upload/common/types.ts b/x-pack/plugins/file_upload/common/types.ts index 3c385bdf8f3df..0fc59e2b525a8 100644 --- a/x-pack/plugins/file_upload/common/types.ts +++ b/x-pack/plugins/file_upload/common/types.ts @@ -7,6 +7,10 @@ import { ES_FIELD_TYPES } from '../../../../src/plugins/data/common'; +export interface HasImportPermission { + hasImportPermission: boolean; +} + export interface InputOverrides { [key: string]: string | undefined; } diff --git a/x-pack/plugins/file_upload/kibana.json b/x-pack/plugins/file_upload/kibana.json index 6d55e4f46d7e8..a1c585e534333 100644 --- a/x-pack/plugins/file_upload/kibana.json +++ b/x-pack/plugins/file_upload/kibana.json @@ -5,5 +5,6 @@ "server": true, "ui": true, "requiredPlugins": ["data", "usageCollection"], + "optionalPlugins": ["security"], "requiredBundles": ["kibanaReact"] } diff --git a/x-pack/plugins/file_upload/public/api/index.ts b/x-pack/plugins/file_upload/public/api/index.ts index 8884c398fa2e6..281537cbbde16 100644 --- a/x-pack/plugins/file_upload/public/api/index.ts +++ b/x-pack/plugins/file_upload/public/api/index.ts @@ -8,12 +8,14 @@ import React from 'react'; import { FileUploadComponentProps, lazyLoadFileUploadModules } from '../lazy_load_bundle'; import type { IImporter, ImportFactoryOptions } from '../importer'; +import { HasImportPermission } from '../../common'; export interface FileUploadStartApi { getFileUploadComponent(): Promise>; importerFactory(format: string, options: ImportFactoryOptions): Promise; getMaxBytes(): number; getMaxBytesFormatted(): string; + hasImportPermission(params: HasImportPermissionParams): Promise; } export async function getFileUploadComponent(): Promise< @@ -30,3 +32,23 @@ export async function importerFactory( const fileUploadModules = await lazyLoadFileUploadModules(); return fileUploadModules.importerFactory(format, options); } + +interface HasImportPermissionParams { + checkCreateIndexPattern: boolean; + checkHasManagePipeline: boolean; + indexName?: string; +} + +export async function hasImportPermission(params: HasImportPermissionParams): Promise { + const fileUploadModules = await lazyLoadFileUploadModules(); + try { + const resp = await fileUploadModules.getHttp().fetch({ + path: `/internal/file_upload/has_import_permission`, + method: 'GET', + query: { ...params }, + }); + return resp.hasImportPermission; + } catch (error) { + return false; + } +} diff --git a/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts b/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts index a61faa5bef0c4..807d2fae52bf8 100644 --- a/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts +++ b/x-pack/plugins/file_upload/public/lazy_load_bundle/index.ts @@ -8,7 +8,9 @@ import React from 'react'; import { FeatureCollection } from 'geojson'; import { IndexPattern } from 'src/plugins/data/public'; +import { HttpStart } from 'src/core/public'; import { IImporter, ImportFactoryOptions, ImportResults } from '../importer'; +import { getHttp } from '../kibana_services'; export interface FileUploadComponentProps { isIndexingTriggered: boolean; @@ -27,6 +29,7 @@ let loadModulesPromise: Promise; interface LazyLoadedFileUploadModules { JsonUploadAndParse: React.ComponentType; importerFactory: (format: string, options: ImportFactoryOptions) => IImporter | undefined; + getHttp: () => HttpStart; } export async function lazyLoadFileUploadModules(): Promise { @@ -40,6 +43,7 @@ export async function lazyLoadFileUploadModules(): Promise new FileUploadPlugin(); +export const plugin = (initializerContext: PluginInitializerContext) => + new FileUploadPlugin(initializerContext); diff --git a/x-pack/plugins/file_upload/server/plugin.ts b/x-pack/plugins/file_upload/server/plugin.ts index 94b4f0368d562..5a4b59fe4f5e6 100644 --- a/x-pack/plugins/file_upload/server/plugin.ts +++ b/x-pack/plugins/file_upload/server/plugin.ts @@ -6,20 +6,27 @@ */ import { i18n } from '@kbn/i18n'; -import { CoreSetup, CoreStart, Plugin } from 'src/core/server'; +import { CoreSetup, CoreStart, Logger, Plugin, PluginInitializerContext } from 'src/core/server'; import { schema } from '@kbn/config-schema'; import { fileUploadRoutes } from './routes'; import { initFileUploadTelemetry } from './telemetry'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; import { UI_SETTING_MAX_FILE_SIZE, MAX_FILE_SIZE } from '../common'; +import { StartDeps } from './types'; interface SetupDeps { usageCollection: UsageCollectionSetup; } export class FileUploadPlugin implements Plugin { - async setup(coreSetup: CoreSetup, plugins: SetupDeps) { - fileUploadRoutes(coreSetup.http.createRouter()); + private readonly _logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this._logger = initializerContext.logger.get(); + } + + async setup(coreSetup: CoreSetup, plugins: SetupDeps) { + fileUploadRoutes(coreSetup, this._logger); coreSetup.uiSettings.register({ [UI_SETTING_MAX_FILE_SIZE]: { diff --git a/x-pack/plugins/file_upload/server/routes.ts b/x-pack/plugins/file_upload/server/routes.ts index 4f4adb29f6b0b..6d7eb77f39069 100644 --- a/x-pack/plugins/file_upload/server/routes.ts +++ b/x-pack/plugins/file_upload/server/routes.ts @@ -6,7 +6,8 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter, IScopedClusterClient } from 'kibana/server'; +import { IScopedClusterClient } from 'kibana/server'; +import { CoreSetup, Logger } from 'src/core/server'; import { MAX_FILE_SIZE_BYTES, IngestPipelineWrapper, @@ -20,6 +21,8 @@ import { importDataProvider } from './import_data'; import { updateTelemetry } from './telemetry'; import { analyzeFileQuerySchema, importFileBodySchema, importFileQuerySchema } from './schemas'; +import { CheckPrivilegesPayload } from '../../security/server'; +import { StartDeps } from './types'; function importData( client: IScopedClusterClient, @@ -37,7 +40,55 @@ function importData( /** * Routes for the file upload. */ -export function fileUploadRoutes(router: IRouter) { +export function fileUploadRoutes(coreSetup: CoreSetup, logger: Logger) { + const router = coreSetup.http.createRouter(); + + router.get( + { + path: '/internal/file_upload/has_import_permission', + validate: { + query: schema.object({ + indexName: schema.maybe(schema.string()), + checkCreateIndexPattern: schema.boolean(), + checkHasManagePipeline: schema.boolean(), + }), + }, + }, + async (context, request, response) => { + try { + const [, pluginsStart] = await coreSetup.getStartServices(); + const { indexName, checkCreateIndexPattern, checkHasManagePipeline } = request.query; + + const authorizationService = pluginsStart.security?.authz; + const requiresAuthz = authorizationService?.mode.useRbacForRequest(request) ?? false; + + if (!authorizationService || !requiresAuthz) { + return response.ok({ body: { hasImportPermission: true } }); + } + + const checkPrivilegesPayload: CheckPrivilegesPayload = { + elasticsearch: { + cluster: checkHasManagePipeline ? ['manage_pipeline'] : [], + index: indexName ? { [indexName]: ['create', 'create_index'] } : {}, + }, + }; + if (checkCreateIndexPattern) { + checkPrivilegesPayload.kibana = [ + authorizationService.actions.savedObject.get('index-pattern', 'create'), + ]; + } + + const checkPrivileges = authorizationService.checkPrivilegesDynamicallyWithRequest(request); + const checkPrivilegesResp = await checkPrivileges(checkPrivilegesPayload); + + return response.ok({ body: { hasImportPermission: checkPrivilegesResp.hasAllRequested } }); + } catch (e) { + logger.warn(`Unable to check import permission, error: ${e.message}`); + return response.ok({ body: { hasImportPermission: false } }); + } + } + ); + /** * @apiGroup FileDataVisualizer * diff --git a/x-pack/plugins/file_upload/server/types.ts b/x-pack/plugins/file_upload/server/types.ts new file mode 100644 index 0000000000000..d23661ebae711 --- /dev/null +++ b/x-pack/plugins/file_upload/server/types.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SecurityPluginStart } from '../..//security/server'; + +export interface StartDeps { + security?: SecurityPluginStart; +} diff --git a/x-pack/plugins/file_upload/tsconfig.json b/x-pack/plugins/file_upload/tsconfig.json index bebb08e6dd5e3..887a05af31174 100644 --- a/x-pack/plugins/file_upload/tsconfig.json +++ b/x-pack/plugins/file_upload/tsconfig.json @@ -11,6 +11,7 @@ "references": [ { "path": "../../../src/core/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, - { "path": "../../../src/plugins/usage_collection/tsconfig.json" } + { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { "path": "../security/tsconfig.json" }, ] } diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index 925358c434e5d..efe16c30935be 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -679,4 +679,205 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(1); }); + + it('should call onload after rerender and onData$ call ', async () => { + const onLoad = jest.fn(); + + expressionRenderer = jest.fn(({ onData$ }) => { + setTimeout(() => { + onData$?.({}); + }, 10); + + return null; + }); + + const embeddable = new Embeddable( + { + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, + editable: true, + getTrigger, + documentToExpression: () => + Promise.resolve({ + ast: { + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }, + errors: undefined, + }), + }, + ({ id: '123', onLoad } as unknown) as LensEmbeddableInput + ); + + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + expect(onLoad).toHaveBeenCalledWith(true); + expect(onLoad).toHaveBeenCalledTimes(1); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + // loading should become false + expect(onLoad).toHaveBeenCalledTimes(2); + expect(onLoad).toHaveBeenNthCalledWith(2, false); + + expect(expressionRenderer).toHaveBeenCalledTimes(1); + + embeddable.updateInput({ + searchSessionId: 'newSession', + }); + embeddable.reload(); + + // loading should become again true + expect(onLoad).toHaveBeenCalledTimes(3); + expect(onLoad).toHaveBeenNthCalledWith(3, true); + + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(expressionRenderer).toHaveBeenCalledTimes(2); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + // loading should again become false + expect(onLoad).toHaveBeenCalledTimes(4); + expect(onLoad).toHaveBeenNthCalledWith(4, false); + }); + + it('should call onFilter event on filter call ', async () => { + const onFilter = jest.fn(); + + expressionRenderer = jest.fn(({ onEvent }) => { + setTimeout(() => { + onEvent?.({ name: 'filter', data: { pings: false } }); + }, 10); + + return null; + }); + + const embeddable = new Embeddable( + { + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, + editable: true, + getTrigger, + documentToExpression: () => + Promise.resolve({ + ast: { + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }, + errors: undefined, + }), + }, + ({ id: '123', onFilter } as unknown) as LensEmbeddableInput + ); + + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + expect(onFilter).toHaveBeenCalledWith({ pings: false }); + expect(onFilter).toHaveBeenCalledTimes(1); + }); + + it('should call onBrush event on brushing', async () => { + const onBrushEnd = jest.fn(); + + expressionRenderer = jest.fn(({ onEvent }) => { + setTimeout(() => { + onEvent?.({ name: 'brush', data: { range: [0, 1] } }); + }, 10); + + return null; + }); + + const embeddable = new Embeddable( + { + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, + editable: true, + getTrigger, + documentToExpression: () => + Promise.resolve({ + ast: { + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }, + errors: undefined, + }), + }, + ({ id: '123', onBrushEnd } as unknown) as LensEmbeddableInput + ); + + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + expect(onBrushEnd).toHaveBeenCalledWith({ range: [0, 1] }); + expect(onBrushEnd).toHaveBeenCalledTimes(1); + }); + + it('should call onTableRowClick event ', async () => { + const onTableRowClick = jest.fn(); + + expressionRenderer = jest.fn(({ onEvent }) => { + setTimeout(() => { + onEvent?.({ name: 'tableRowContextMenuClick', data: { name: 'test' } }); + }, 10); + + return null; + }); + + const embeddable = new Embeddable( + { + timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter, + attributeService, + expressionRenderer, + basePath, + indexPatternService: {} as IndexPatternsContract, + editable: true, + getTrigger, + documentToExpression: () => + Promise.resolve({ + ast: { + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }, + errors: undefined, + }), + }, + ({ id: '123', onTableRowClick } as unknown) as LensEmbeddableInput + ); + + await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput); + embeddable.render(mountpoint); + + await new Promise((resolve) => setTimeout(resolve, 20)); + + expect(onTableRowClick).toHaveBeenCalledWith({ name: 'test' }); + expect(onTableRowClick).toHaveBeenCalledTimes(1); + }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index bfeb645d7bd5f..b395352b61477 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -45,6 +45,9 @@ import { isLensBrushEvent, isLensFilterEvent, isLensTableRowContextMenuClickEvent, + LensBrushEvent, + LensFilterEvent, + LensTableRowContextMenuEvent, } from '../../types'; import { IndexPatternsContract } from '../../../../../../src/plugins/data/public'; @@ -63,6 +66,10 @@ interface LensBaseEmbeddableInput extends EmbeddableInput { renderMode?: RenderMode; style?: React.CSSProperties; className?: string; + onBrushEnd?: (data: LensBrushEvent['data']) => void; + onLoad?: (isLoading: boolean) => void; + onFilter?: (data: LensFilterEvent['data']) => void; + onTableRowClick?: (data: LensTableRowContextMenuEvent['data']) => void; } export type LensByValueInput = { @@ -103,6 +110,8 @@ export class Embeddable private isInitialized = false; private activeData: Partial | undefined; private errors: ErrorMessage[] | undefined; + private inputReloadSubscriptions: Subscription[]; + private isDestroyed?: boolean; private externalSearchContext: { timeRange?: TimeRange; @@ -133,65 +142,76 @@ export class Embeddable const input$ = this.getInput$(); + this.inputReloadSubscriptions = []; + // Lens embeddable does not re-render when embeddable input changes in // general, to improve performance. This line makes sure the Lens embeddable // re-renders when anything in ".dynamicActions" (e.g. drilldowns) changes. - input$ - .pipe( - map((input) => input.enhancements?.dynamicActions), - distinctUntilChanged((a, b) => isEqual(a, b)), - skip(1) - ) - .subscribe((input) => { - this.reload(); - }); + this.inputReloadSubscriptions.push( + input$ + .pipe( + map((input) => input.enhancements?.dynamicActions), + distinctUntilChanged((a, b) => isEqual(a, b)), + skip(1) + ) + .subscribe((input) => { + this.reload(); + }) + ); // Lens embeddable does not re-render when embeddable input changes in // general, to improve performance. This line makes sure the Lens embeddable // re-renders when dashboard view mode switches between "view/edit". This is // needed to see the changes to ".dynamicActions" (e.g. drilldowns) when // dashboard's mode is toggled. - input$ - .pipe( - map((input) => input.viewMode), - distinctUntilChanged(), - skip(1) - ) - .subscribe((input) => { - this.reload(); - }); + this.inputReloadSubscriptions.push( + input$ + .pipe( + map((input) => input.viewMode), + distinctUntilChanged(), + skip(1) + ) + .subscribe((input) => { + this.reload(); + }) + ); // Re-initialize the visualization if either the attributes or the saved object id changes - input$ - .pipe( - distinctUntilChanged((a, b) => - isEqual( - ['attributes' in a && a.attributes, 'savedObjectId' in a && a.savedObjectId], - ['attributes' in b && b.attributes, 'savedObjectId' in b && b.savedObjectId] - ) - ), - skip(1) - ) - .subscribe(async (input) => { - await this.initializeSavedVis(input); - this.reload(); - }); + + this.inputReloadSubscriptions.push( + input$ + .pipe( + distinctUntilChanged((a, b) => + isEqual( + ['attributes' in a && a.attributes, 'savedObjectId' in a && a.savedObjectId], + ['attributes' in b && b.attributes, 'savedObjectId' in b && b.savedObjectId] + ) + ), + skip(1) + ) + .subscribe(async (input) => { + await this.initializeSavedVis(input); + this.reload(); + }) + ); // Update search context and reload on changes related to search - this.getUpdated$() - .pipe(map(() => this.getInput())) - .pipe( - distinctUntilChanged((a, b) => - isEqual( - [a.filters, a.query, a.timeRange, a.searchSessionId], - [b.filters, b.query, b.timeRange, b.searchSessionId] - ) - ), - skip(1) - ) - .subscribe(async (input) => { - this.onContainerStateChanged(input); - }); + this.inputReloadSubscriptions.push( + this.getUpdated$() + .pipe(map(() => this.getInput())) + .pipe( + distinctUntilChanged((a, b) => + isEqual( + [a.filters, a.query, a.timeRange, a.searchSessionId], + [b.filters, b.query, b.timeRange, b.searchSessionId] + ) + ), + skip(1) + ) + .subscribe(async (input) => { + this.onContainerStateChanged(input); + }) + ); } public supportedTriggers() { @@ -222,7 +242,7 @@ export class Embeddable this.onFatalError(e); return false; }); - if (!attributes) { + if (!attributes || this.isDestroyed) { return; } this.savedVis = { @@ -268,6 +288,10 @@ export class Embeddable inspectorAdapters?: Partial | undefined ) => { this.activeData = inspectorAdapters; + if (this.input.onLoad) { + // once onData$ is get's called from expression renderer, loading becomes false + this.input.onLoad(false); + } }; /** @@ -277,9 +301,12 @@ export class Embeddable */ render(domNode: HTMLElement | Element) { this.domNode = domNode; - if (!this.savedVis || !this.isInitialized) { + if (!this.savedVis || !this.isInitialized || this.isDestroyed) { return; } + if (this.input.onLoad) { + this.input.onLoad(true); + } const input = this.getInput(); render( 0) { + this.inputReloadSubscriptions.forEach((reloadSub) => { + reloadSub.unsubscribe(); + }); + } if (this.domNode) { unmountComponentAtNode(this.domNode); } diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/config.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/config.tsx index 4b3f8967be3bb..49f35c491ccf0 100644 --- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/config.tsx +++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/config.tsx @@ -9,12 +9,24 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { LayerWizard, RenderWizardArguments } from '../../layers/layer_wizard_registry'; import { ClientFileCreateSourceEditor, INDEX_SETUP_STEP_ID, INDEXING_STEP_ID } from './wizard'; +import { getFileUpload } from '../../../kibana_services'; export const uploadLayerWizardConfig: LayerWizard = { categories: [], description: i18n.translate('xpack.maps.fileUploadWizard.description', { defaultMessage: 'Index GeoJSON data in Elasticsearch', }), + disabledReason: i18n.translate('xpack.maps.fileUploadWizard.disabledDesc', { + defaultMessage: + 'Unable to upload files, you are missing the Kibana privilege "Index Pattern Management".', + }), + getIsDisabled: async () => { + const hasImportPermission = await getFileUpload().hasImportPermission({ + checkCreateIndexPattern: true, + checkHasManagePipeline: false, + }); + return !hasImportPermission; + }, icon: 'importAction', prerequisiteSteps: [ { diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx index 30e3317a3a3cf..a6ff14d20f238 100644 --- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx @@ -15,7 +15,7 @@ import { DEFAULT_MAX_RESULT_WINDOW, SCALING_TYPES, } from '../../../../common/constants'; -import { getFileUploadComponent } from '../../../kibana_services'; +import { getFileUpload } from '../../../kibana_services'; import { GeoJsonFileSource } from '../../sources/geojson_file_source'; import { VectorLayer } from '../../layers/vector_layer'; import { createDefaultLayerDescriptor } from '../../sources/es_search_source'; @@ -65,7 +65,7 @@ export class ClientFileCreateSourceEditor extends Component pluginsStart.data.indexPatterns; export const getAutocompleteService = () => pluginsStart.data.autocomplete; export const getInspector = () => pluginsStart.inspector; -export const getFileUploadComponent = async () => { - return await pluginsStart.fileUpload.getFileUploadComponent(); -}; +export const getFileUpload = () => pluginsStart.fileUpload; export const getUiSettings = () => coreStart.uiSettings; export const getIsDarkMode = () => getUiSettings().get('theme:darkMode', false); export const getIndexPatternSelectComponent = () => pluginsStart.data.ui.IndexPatternSelect; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js index 90c2b2bf18639..b184913e51b14 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js @@ -20,13 +20,7 @@ import { FileCouldNotBeRead, FileTooLarge } from './file_error_callouts'; import { EditFlyout } from '../edit_flyout'; import { ExplanationFlyout } from '../explanation_flyout'; import { ImportView } from '../import_view'; -import { - DEFAULT_LINES_TO_SAMPLE, - readFile, - createUrlOverrides, - processResults, - hasImportPermission, -} from '../utils'; +import { DEFAULT_LINES_TO_SAMPLE, readFile, createUrlOverrides, processResults } from '../utils'; import { getFileUpload } from '../../../../util/dependency_cache'; import { MODE } from './constants'; @@ -67,7 +61,10 @@ export class FileDataVisualizerView extends Component { // check the user has the correct permission to import data. // note, calling hasImportPermission with no arguments just checks the // cluster privileges, the user will still need index privileges to create and ingest - const hasPermissionToImport = await hasImportPermission(); + const hasPermissionToImport = await getFileUpload().hasImportPermission({ + checkCreateIndexPattern: false, + checkHasManagePipeline: true, + }); this.setState({ hasPermissionToImport }); } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js index 28f4f2e2ba9e4..04175f46c9201 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js @@ -35,7 +35,6 @@ import { import { ExperimentalBadge } from '../experimental_badge'; import { getIndexPatternNames, loadIndexPatterns } from '../../../../util/index_utils'; import { ml } from '../../../../services/ml_api_service'; -import { hasImportPermission } from '../utils'; const DEFAULT_TIME_FIELD = '@timestamp'; const DEFAULT_INDEX_SETTINGS = { number_of_shards: 1 }; @@ -124,7 +123,13 @@ export class ImportView extends Component { }, async () => { // check to see if the user has permission to create and ingest data into the specified index - if ((await hasImportPermission(index)) === false) { + if ( + (await getFileUpload().hasImportPermission({ + checkCreateIndexPattern: createIndexPattern, + checkHasManagePipeline: true, + indexName: index, + })) === false + ) { errors.push( i18n.translate('xpack.ml.fileDatavisualizer.importView.importPermissionError', { defaultMessage: diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts index 209f2ef5ad174..cbefc12833d2d 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts @@ -5,10 +5,4 @@ * 2.0. */ -export { - createUrlOverrides, - hasImportPermission, - processResults, - readFile, - DEFAULT_LINES_TO_SAMPLE, -} from './utils'; +export { createUrlOverrides, processResults, readFile, DEFAULT_LINES_TO_SAMPLE } from './utils'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts index 85ca27cdf90ff..49e5da565b927 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts @@ -6,7 +6,6 @@ */ import { isEqual } from 'lodash'; -import { ml } from '../../../../services/ml_api_service'; import { AnalysisResult, InputOverrides } from '../../../../../../../file_upload/common'; import { MB } from '../../../../../../../file_upload/public'; @@ -136,27 +135,3 @@ export function processResults({ results, overrides }: AnalysisResult) { linesToSample, }; } - -/** - * A check for the minimum privileges needed to create and ingest data into an index. - * If called with no indexName, the check will just look for the minimum cluster privileges. - * @param {string} indexName - * @returns {Promise} - */ -export async function hasImportPermission(indexName: string) { - const priv: { cluster: string[]; index?: any } = { - cluster: ['cluster:admin/ingest/pipeline/put'], - }; - - if (indexName !== undefined) { - priv.index = [ - { - names: [indexName], - privileges: ['indices:data/write/bulk', 'indices:data/write/index', 'indices:admin/create'], - }, - ]; - } - - const resp = await ml.hasPrivileges(priv); - return resp.securityDisabled === true || resp.has_all_requested === true; -} diff --git a/x-pack/plugins/monitoring/public/alerts/configuration.tsx b/x-pack/plugins/monitoring/public/alerts/configuration.tsx index 59411d431d83a..5416095671d71 100644 --- a/x-pack/plugins/monitoring/public/alerts/configuration.tsx +++ b/x-pack/plugins/monitoring/public/alerts/configuration.tsx @@ -12,7 +12,7 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSwitch } from '@elastic/eui'; import { CommonAlert } from '../../common/types/alerts'; import { Legacy } from '../legacy_shims'; import { hideBottomBar, showBottomBar } from '../lib/setup_mode'; -import { BASE_ALERT_API_PATH } from '../../../alerting/common'; +import { LEGACY_BASE_ALERT_API_PATH } from '../../../alerting/common'; interface Props { alert: CommonAlert; @@ -28,7 +28,7 @@ export const AlertConfiguration: React.FC = (props: Props) => { async function disableAlert() { setIsSaving(true); try { - await Legacy.shims.http.post(`${BASE_ALERT_API_PATH}/alert/${alert.id}/_disable`); + await Legacy.shims.http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alert.id}/_disable`); } catch (err) { Legacy.shims.toastNotifications.addDanger({ title: i18n.translate('xpack.monitoring.alerts.panel.disableAlert.errorTitle', { @@ -42,7 +42,7 @@ export const AlertConfiguration: React.FC = (props: Props) => { async function enableAlert() { setIsSaving(true); try { - await Legacy.shims.http.post(`${BASE_ALERT_API_PATH}/alert/${alert.id}/_enable`); + await Legacy.shims.http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alert.id}/_enable`); } catch (err) { Legacy.shims.toastNotifications.addDanger({ title: i18n.translate('xpack.monitoring.alerts.panel.enableAlert.errorTitle', { @@ -56,7 +56,7 @@ export const AlertConfiguration: React.FC = (props: Props) => { async function muteAlert() { setIsSaving(true); try { - await Legacy.shims.http.post(`${BASE_ALERT_API_PATH}/alert/${alert.id}/_mute_all`); + await Legacy.shims.http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alert.id}/_mute_all`); } catch (err) { Legacy.shims.toastNotifications.addDanger({ title: i18n.translate('xpack.monitoring.alerts.panel.muteAlert.errorTitle', { @@ -70,7 +70,7 @@ export const AlertConfiguration: React.FC = (props: Props) => { async function unmuteAlert() { setIsSaving(true); try { - await Legacy.shims.http.post(`${BASE_ALERT_API_PATH}/alert/${alert.id}/_unmute_all`); + await Legacy.shims.http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alert.id}/_unmute_all`); } catch (err) { Legacy.shims.toastNotifications.addDanger({ title: i18n.translate('xpack.monitoring.alerts.panel.ummuteAlert.errorTitle', { diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index fbe487f240699..bb80d84210a48 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -116,7 +116,7 @@ export class BaseAlert { alertsClient: AlertsClient, actionsClient: ActionsClient, actions: AlertEnableAction[] - ): Promise> { + ): Promise> { const existingAlertData = await alertsClient.find({ options: { search: this.alertOptions.id, diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts index b452d22506a1c..01ab392e0563c 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts @@ -13,7 +13,7 @@ import { ALERT_ACTION_TYPE_LOG } from '../../../../../common/constants'; import { ActionResult } from '../../../../../../actions/common'; import { AlertingSecurity } from '../../../../lib/elasticsearch/verify_alerting_security'; import { disableWatcherClusterAlerts } from '../../../../lib/alerts/disable_watcher_cluster_alerts'; -import { Alert, AlertTypeParams } from '../../../../../../alerting/common'; +import { AlertTypeParams, SanitizedAlert } from '../../../../../../alerting/common'; const DEFAULT_SERVER_LOG_NAME = 'Monitoring: Write to Kibana log'; @@ -77,7 +77,7 @@ export function enableAlertsRoute(_server: unknown, npRoute: RouteDependencies) }, ]; - let createdAlerts: Array> = []; + let createdAlerts: Array> = []; const disabledWatcherClusterAlerts = await disableWatcherClusterAlerts( npRoute.cluster.asScoped(request).callAsCurrentUser, npRoute.logger diff --git a/x-pack/plugins/security/server/authorization/index.ts b/x-pack/plugins/security/server/authorization/index.ts index 1eca1de2e6814..6cbb4d10c75e4 100644 --- a/x-pack/plugins/security/server/authorization/index.ts +++ b/x-pack/plugins/security/server/authorization/index.ts @@ -9,3 +9,4 @@ export { Actions } from './actions'; export { AuthorizationService, AuthorizationServiceSetup } from './authorization_service'; export { CheckSavedObjectsPrivileges } from './check_saved_objects_privileges'; export { featurePrivilegeIterator } from './privileges'; +export { CheckPrivilegesPayload } from './types'; diff --git a/x-pack/plugins/security/server/index.ts b/x-pack/plugins/security/server/index.ts index 7120f480bbc42..6412562af8a41 100644 --- a/x-pack/plugins/security/server/index.ts +++ b/x-pack/plugins/security/server/index.ts @@ -26,6 +26,7 @@ export type { InvalidateAPIKeyResult, GrantAPIKeyResult, } from './authentication'; +export type { CheckPrivilegesPayload } from './authorization'; export { LegacyAuditLogger, AuditLogger, diff --git a/x-pack/plugins/security_solution/public/management/common/routing.test.ts b/x-pack/plugins/security_solution/public/management/common/routing.test.ts index 1d06a78c2aa69..a1662c21012be 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.test.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.test.ts @@ -127,6 +127,7 @@ describe('routing', () => { page_size: 20, show: 'create', view_type: 'list', + filter: '', }; expect(getTrustedAppsListPath(location)).toEqual( diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index bf754720f314b..82aa96714d70e 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -109,6 +109,7 @@ const normalizeTrustedAppsPageLocation = ( ...(!isDefaultOrMissing(location.view_type, 'grid') ? { view_type: location.view_type } : {}), ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), ...(!isDefaultOrMissing(location.id, undefined) ? { id: location.id } : {}), + ...(!isDefaultOrMissing(location.filter, '') ? { filter: location.filter } : ''), }; } else { return {}; @@ -141,9 +142,14 @@ const extractPageSize = (query: querystring.ParsedUrlQuery): number => { return MANAGEMENT_PAGE_SIZE_OPTIONS.includes(pageSize) ? pageSize : MANAGEMENT_DEFAULT_PAGE_SIZE; }; +const extractFilter = (query: querystring.ParsedUrlQuery): string => { + return extractFirstParamValue(query, 'filter') || ''; +}; + export const extractListPaginationParams = (query: querystring.ParsedUrlQuery) => ({ page_index: extractPageIndex(query), page_size: extractPageSize(query), + filter: extractFilter(query), }); export const extractTrustedAppsListPageLocation = ( diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts index 1c1fca4b55abc..e37b0f262603d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts @@ -22,6 +22,7 @@ export interface TrustedAppsListData { pageSize: number; timestamp: number; totalItemsCount: number; + filter: string; } export type ViewType = 'list' | 'grid'; @@ -33,6 +34,7 @@ export interface TrustedAppsListPageLocation { show?: 'create' | 'edit'; /** Used for editing. The ID of the selected trusted app */ id?: string; + filter: string; } export interface TrustedAppsListPageState { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/builders.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/builders.ts index ece2c9e29750f..5a22badec9afb 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/builders.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/builders.ts @@ -56,6 +56,7 @@ export const initialTrustedAppsPageState = (): TrustedAppsListPageState => ({ show: undefined, id: undefined, view_type: 'grid', + filter: '', }, active: false, }); 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 7f940f14f9c6c..71a49caf66fd6 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 @@ -46,6 +46,7 @@ import { getLastLoadedListResourceState, getCurrentLocationPageIndex, getCurrentLocationPageSize, + getCurrentLocationFilter, needsRefreshOfListData, getCreationSubmissionResourceState, getCreationDialogFormEntry, @@ -63,6 +64,7 @@ import { getListItems, editItemState, } from './selectors'; +import { parseQueryFilterToKQL } from './utils'; import { toUpdateTrustedApp } from '../../../../../common/endpoint/service/trusted_apps/to_update_trusted_app'; const createTrustedAppsListResourceStateChangedAction = ( @@ -90,9 +92,12 @@ const refreshListIfNeeded = async ( try { const pageIndex = getCurrentLocationPageIndex(store.getState()); const pageSize = getCurrentLocationPageSize(store.getState()); + const filter = getCurrentLocationFilter(store.getState()); + const response = await trustedAppsService.getTrustedAppsList({ page: pageIndex + 1, per_page: pageSize, + kuery: parseQueryFilterToKQL(filter) || undefined, }); store.dispatch( @@ -104,6 +109,7 @@ const refreshListIfNeeded = async ( pageSize, totalItemsCount: response.total, timestamp: Date.now(), + filter, }, }) ); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts index 6965172ef773d..58193eea3de52 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts @@ -31,7 +31,7 @@ describe('reducer', () => { initialState, createUserChangedUrlAction( '/trusted_apps', - '?page_index=5&page_size=50&show=create&view_type=list' + '?page_index=5&page_size=50&show=create&view_type=list&filter=test' ) ); @@ -43,6 +43,7 @@ describe('reducer', () => { show: 'create', view_type: 'list', id: undefined, + filter: 'test', }, active: true, }); @@ -50,7 +51,10 @@ describe('reducer', () => { it('extracts default pagination parameters when invalid provided', () => { const result = trustedAppsPageReducer( - { ...initialState, location: { page_index: 5, page_size: 50, view_type: 'grid' } }, + { + ...initialState, + location: { page_index: 5, page_size: 50, view_type: 'grid', filter: '' }, + }, createUserChangedUrlAction('/trusted_apps', '?page_index=b&page_size=60&show=a&view_type=c') ); @@ -59,7 +63,10 @@ describe('reducer', () => { it('extracts default pagination parameters when none provided', () => { const result = trustedAppsPageReducer( - { ...initialState, location: { page_index: 5, page_size: 50, view_type: 'grid' } }, + { + ...initialState, + location: { page_index: 5, page_size: 50, view_type: 'grid', filter: '' }, + }, createUserChangedUrlAction('/trusted_apps') ); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts index 3616f79489b4a..4d8ce097a7263 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts @@ -93,6 +93,7 @@ describe('selectors', () => { page_index: 0, page_size: 10, view_type: 'grid', + filter: '', }; expect(needsRefreshOfListData({ ...initialState, listView, active: true, location })).toBe( @@ -174,6 +175,7 @@ describe('selectors', () => { page_index: 3, page_size: 10, view_type: 'grid', + filter: '', }; expect(getCurrentLocationPageIndex({ ...initialState, location })).toBe(3); @@ -186,6 +188,7 @@ describe('selectors', () => { page_index: 0, page_size: 20, view_type: 'grid', + filter: '', }; expect(getCurrentLocationPageSize({ ...initialState, location })).toBe(20); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts index 7c131c3eaa7a9..43506f98193a0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts @@ -30,14 +30,14 @@ export const needsRefreshOfListData = (state: Immutable { return ( data.pageIndex === location.page_index && data.pageSize === location.page_size && - data.timestamp >= freshDataTimestamp + data.timestamp >= freshDataTimestamp && + data.filter === location.filter ); }) ); @@ -69,6 +69,10 @@ export const getCurrentLocationPageSize = (state: Immutable): string => { + return state.location.filter; +}; + export const getListTotalItemsCount = (state: Immutable): number => { return getLastLoadedResourceState(state.listView.listResourceState)?.data.totalItemsCount || 0; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.test.ts new file mode 100644 index 0000000000000..14a5b3e809a53 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.test.ts @@ -0,0 +1,26 @@ +/* + * 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 { parseQueryFilterToKQL } from './utils'; + +describe('utils', () => { + describe('parseQueryFilterToKQL', () => { + it('should parse simple query without term', () => { + expect(parseQueryFilterToKQL('')).toBe(''); + }); + it('should parse simple query with term', () => { + expect(parseQueryFilterToKQL('simpleQuery')).toBe( + 'exception-list-agnostic.attributes.name:*simpleQuery* OR exception-list-agnostic.attributes.description:*simpleQuery* OR exception-list-agnostic.attributes.entries.value:*simpleQuery* OR exception-list-agnostic.attributes.entries.entries.value:*simpleQuery*' + ); + }); + it('should parse complex query with term', () => { + expect(parseQueryFilterToKQL('complex query')).toBe( + 'exception-list-agnostic.attributes.name:*complex* *query* OR exception-list-agnostic.attributes.description:*complex* *query* OR exception-list-agnostic.attributes.entries.value:*complex* *query* OR exception-list-agnostic.attributes.entries.entries.value:*complex* *query*' + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.ts new file mode 100644 index 0000000000000..a2f19a6003056 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/utils.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const parseQueryFilterToKQL = (filter: string): string => { + if (!filter) return ''; + const kuery = [`name`, `description`, `entries.value`, `entries.entries.value`] + .map( + (field) => + `exception-list-agnostic.attributes.${field}:*${filter.trim().replace(/\s/gm, '* *')}*` + ) + .join(' OR '); + + return kuery; +}; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts index faffc6b04a0cd..7783ac96e192d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts @@ -79,6 +79,7 @@ export const createTrustedAppsListData = ( pageIndex: fullPagination.pageIndex, totalItemsCount: fullPagination.totalItemCount, timestamp, + filter: '', }; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/search_bar/index.test.tsx new file mode 100644 index 0000000000000..f12c979c2afe4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/search_bar/index.test.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { mount } from 'enzyme'; +import React from 'react'; + +import { SearchBar } from '.'; + +let onSearchMock: jest.Mock; + +interface EuiFieldSearchPropsFake { + onSearch(value: string): void; +} + +describe('Search bar', () => { + beforeEach(() => { + onSearchMock = jest.fn(); + }); + + const getElement = (defaultValue: string = '') => ( + + ); + + 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="trustedAppSearchField"]').first().props() + .defaultValue; + expect(defaultValue).toBe(expectedDefaultValue); + }); + + it('should dispatch search action when submit search field', () => { + const expectedDefaultValue = 'this is a default value'; + const element = mount(getElement()); + expect(onSearchMock).toHaveBeenCalledTimes(0); + const searchFieldProps = element + .find('[data-test-subj="trustedAppSearchField"]') + .first() + .props() as EuiFieldSearchPropsFake; + + searchFieldProps.onSearch(expectedDefaultValue); + + expect(onSearchMock).toHaveBeenCalledTimes(1); + expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue); + }); + + it('should dispatch search action when click on button', () => { + const expectedDefaultValue = 'this is a default value'; + const element = mount(getElement(expectedDefaultValue)); + expect(onSearchMock).toHaveBeenCalledTimes(0); + + element.find('[data-test-subj="trustedAppSearchButton"]').first().simulate('click'); + expect(onSearchMock).toHaveBeenCalledTimes(1); + expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue); + }); +}); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/search_bar/index.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/search_bar/index.tsx new file mode 100644 index 0000000000000..6fc6b2af4d585 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/search_bar/index.tsx @@ -0,0 +1,55 @@ +/* + * 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, { memo, useCallback, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +export interface SearchBarProps { + defaultValue?: string; + onSearch(value: string): void; +} + +export const SearchBar = memo(({ defaultValue = '', onSearch }) => { + const [query, setQuery] = useState(defaultValue); + + const handleOnChangeSearchField = useCallback( + (ev: React.ChangeEvent) => setQuery(ev.target.value), + [setQuery] + ); + const handleOnSearch = useCallback(() => onSearch(query), [query, onSearch]); + + return ( + + + + + + + {i18n.translate('xpack.securitySolution.trustedapps.list.search.button', { + defaultMessage: 'Refresh', + })} + + + + ); +}); + +SearchBar.displayName = 'SearchBar'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index 6093a8d66e0d8..8a1b1ccfa5173 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -860,4 +860,35 @@ describe('When on the Trusted Apps Page', () => { expect(await renderResult.findByTestId('trustedAppEmptyState')).not.toBeNull(); }); }); + + describe('and the search is dispatched', () => { + const renderWithListData = async () => { + const result = render(); + await act(async () => { + await waitForAction('trustedAppsListResourceStateChanged'); + }); + return result; + }; + + beforeEach(() => mockListApis(coreStart.http)); + + it('search bar is filled with query params', async () => { + reactTestingLibrary.act(() => { + history.push('/trusted_apps?filter=test'); + }); + const result = await renderWithListData(); + expect(result.getByDisplayValue('test')).not.toBeNull(); + }); + + it('search action is dispatched', async () => { + reactTestingLibrary.act(() => { + history.push('/trusted_apps?filter=test'); + }); + const result = await renderWithListData(); + await act(async () => { + fireEvent.click(result.getByTestId('trustedAppSearchButton')); + await waitForAction('userChangedUrl'); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index feba6eca04856..1bf44769c15b4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -39,6 +39,7 @@ import { TrustedAppsListPageRouteState } from '../../../../../common/endpoint/ty import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; import { ABOUT_TRUSTED_APPS } from './translations'; import { EmptyState } from './components/empty_state'; +import { SearchBar } from './components/search_bar'; export const TrustedAppsPage = memo(() => { const { state: routeState } = useLocation(); @@ -57,6 +58,7 @@ export const TrustedAppsPage = memo(() => { const handleViewTypeChange = useTrustedAppsNavigateCallback((viewType: ViewType) => ({ view_type: viewType, })); + const handleOnSearch = useTrustedAppsNavigateCallback((query: string) => ({ filter: query })); const showCreateFlyout = !!location.show; @@ -94,12 +96,14 @@ export const TrustedAppsPage = memo(() => { /> )} + {doEntriesExist ? ( + > => +}: CreateNotificationParams): Promise> => alertsClient.create({ data: { name, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index e020810b6d03a..a654dd6a10e32 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -6,7 +6,7 @@ */ import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; -import { Alert } from '../../../../../alerting/common'; +import { SanitizedAlert } from '../../../../../alerting/common'; import { SERVER_APP_ID, SIGNALS_ID } from '../../../../common/constants'; import { CreateRulesOptions } from './types'; import { addTags } from './add_tags'; @@ -62,7 +62,7 @@ export const createRules = async ({ version, exceptionsList, actions, -}: CreateRulesOptions): Promise> => { +}: CreateRulesOptions): Promise> => { return alertsClient.create({ data: { name, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts index 31df1dd4193a4..7efd63cc67722 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -6,7 +6,7 @@ */ import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; -import { Alert, AlertTypeParams } from '../../../../../alerting/common'; +import { SanitizedAlert, AlertTypeParams } from '../../../../../alerting/common'; import { AlertsClient } from '../../../../../alerting/server'; import { createRules } from './create_rules'; import { PartialFilter } from '../types'; @@ -15,8 +15,8 @@ export const installPrepackagedRules = ( alertsClient: AlertsClient, rules: AddPrepackagedRulesSchemaDecoded[], outputIndex: string -): Array>> => - rules.reduce>>>((acc, rule) => { +): Array>> => + rules.reduce>>>((acc, rule) => { const { anomaly_threshold: anomalyThreshold, author, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 2d0b2e99c10a6..8ac1fbaec403b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; -export { BASE_ALERT_API_PATH } from '../../../../alerting/common'; +export { LEGACY_BASE_ALERT_API_PATH } from '../../../../alerting/common'; export { BASE_ACTION_API_PATH } from '../../../../actions/common'; export type Section = 'connectors' | 'rules'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts index 744e6472a9194..80ff415582191 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts @@ -11,7 +11,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { pick } from 'lodash'; import { alertStateSchema, AlertingFrameworkHealth } from '../../../../alerting/common'; -import { BASE_ALERT_API_PATH } from '../constants'; +import { LEGACY_BASE_ALERT_API_PATH } from '../constants'; import { Alert, AlertAggregations, @@ -24,7 +24,7 @@ import { } from '../../types'; export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`); + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/list_alert_types`); } export async function loadAlert({ @@ -34,7 +34,7 @@ export async function loadAlert({ http: HttpSetup; alertId: string; }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/alert/${alertId}`); + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alertId}`); } type EmptyHttpResponse = ''; @@ -46,7 +46,7 @@ export async function loadAlertState({ alertId: string; }): Promise { return await http - .get(`${BASE_ALERT_API_PATH}/alert/${alertId}/state`) + .get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alertId}/state`) .then((state: AlertTaskState | EmptyHttpResponse) => (state ? state : {})) .then((state: AlertTaskState) => { return pipe( @@ -65,7 +65,7 @@ export async function loadAlertInstanceSummary({ http: HttpSetup; alertId: string; }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/alert/${alertId}/_instance_summary`); + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/alert/${alertId}/_instance_summary`); } export const mapFiltersToKql = ({ @@ -121,7 +121,7 @@ export async function loadAlerts({ data: Alert[]; }> { const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, alertStatusesFilter }); - return await http.get(`${BASE_ALERT_API_PATH}/_find`, { + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/_find`, { query: { page: page.index + 1, per_page: page.size, @@ -149,7 +149,7 @@ export async function loadAlertAggregations({ alertStatusesFilter?: string[]; }): Promise { const filters = mapFiltersToKql({ typesFilter, actionTypesFilter, alertStatusesFilter }); - return await http.get(`${BASE_ALERT_API_PATH}/_aggregate`, { + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/_aggregate`, { query: { search_fields: searchText ? JSON.stringify(['name', 'tags']) : undefined, search: searchText, @@ -168,7 +168,7 @@ export async function deleteAlerts({ }): Promise<{ successes: string[]; errors: string[] }> { const successes: string[] = []; const errors: string[] = []; - await Promise.all(ids.map((id) => http.delete(`${BASE_ALERT_API_PATH}/alert/${id}`))).then( + await Promise.all(ids.map((id) => http.delete(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}`))).then( function (fulfilled) { successes.push(...fulfilled); }, @@ -189,7 +189,7 @@ export async function createAlert({ 'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds' | 'executionStatus' >; }): Promise { - return await http.post(`${BASE_ALERT_API_PATH}/alert`, { + return await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert`, { body: JSON.stringify(alert), }); } @@ -206,7 +206,7 @@ export async function updateAlert({ >; id: string; }): Promise { - return await http.put(`${BASE_ALERT_API_PATH}/alert/${id}`, { + return await http.put(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}`, { body: JSON.stringify( pick(alert, ['throttle', 'name', 'tags', 'schedule', 'params', 'actions', 'notifyWhen']) ), @@ -214,7 +214,7 @@ export async function updateAlert({ } export async function enableAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_enable`); + await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_enable`); } export async function enableAlerts({ @@ -228,7 +228,7 @@ export async function enableAlerts({ } export async function disableAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_disable`); + await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_disable`); } export async function disableAlerts({ @@ -250,7 +250,7 @@ export async function muteAlertInstance({ instanceId: string; http: HttpSetup; }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_mute`); + await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_mute`); } export async function unmuteAlertInstance({ @@ -262,11 +262,11 @@ export async function unmuteAlertInstance({ instanceId: string; http: HttpSetup; }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_unmute`); + await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/alert_instance/${instanceId}/_unmute`); } export async function muteAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_mute_all`); + await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_mute_all`); } export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup }): Promise { @@ -274,7 +274,7 @@ export async function muteAlerts({ ids, http }: { ids: string[]; http: HttpSetup } export async function unmuteAlert({ id, http }: { id: string; http: HttpSetup }): Promise { - await http.post(`${BASE_ALERT_API_PATH}/alert/${id}/_unmute_all`); + await http.post(`${LEGACY_BASE_ALERT_API_PATH}/alert/${id}/_unmute_all`); } export async function unmuteAlerts({ @@ -292,5 +292,5 @@ export async function alertingFrameworkHealth({ }: { http: HttpSetup; }): Promise { - return await http.get(`${BASE_ALERT_API_PATH}/_health`); + return await http.get(`${LEGACY_BASE_ALERT_API_PATH}/_health`); } diff --git a/x-pack/test/accessibility/apps/security_solution.ts b/x-pack/test/accessibility/apps/security_solution.ts index 0ee4e88d712c8..5fe1fec72525c 100644 --- a/x-pack/test/accessibility/apps/security_solution.ts +++ b/x-pack/test/accessibility/apps/security_solution.ts @@ -14,7 +14,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const toasts = getService('toasts'); const testSubjects = getService('testSubjects'); - describe('Security Solution', () => { + // FLAKY: https://github.com/elastic/kibana/issues/95707 + describe.skip('Security Solution', () => { before(async () => { await security.testUser.setRoles(['superuser'], false); await common.navigateToApp('security'); diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts index 617e430df13e7..805f4440909ec 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/basic_noop_alert_type.ts @@ -15,7 +15,7 @@ export default function basicAlertTest({ getService }: FtrProviderContext) { describe('basic alert', () => { it('should return 200 when creating a basic license alert', async () => { await supertest - .post(`/api/alerts/alert`) + .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts index 211d1acb2a005..50e8760f2d8f5 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts @@ -15,9 +15,9 @@ export default function emailTest({ getService }: FtrProviderContext) { describe('create gold noop alert', () => { it('should return 403 when creating an gold alert', async () => { await supertest - .post(`/api/alerts/alert`) + .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ alertTypeId: 'test.gold.noop' })) + .send(getTestAlertData({ rule_type_id: 'test.gold.noop' })) .expect(403, { statusCode: 403, error: 'Forbidden', diff --git a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts index fab4518b99b22..540f8f4d1cad9 100644 --- a/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts +++ b/x-pack/test/alerting_api_integration/common/lib/alert_utils.ts @@ -68,7 +68,7 @@ export class AlertUtils { public getEnableRequest(alertId: string) { const request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert/${alertId}/_enable`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}/_enable`) .set('kbn-xsrf', 'foo'); if (this.user) { return request.auth(this.user.username, this.user.password); @@ -78,7 +78,7 @@ export class AlertUtils { public getDisableRequest(alertId: string) { const request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert/${alertId}/_disable`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}/_disable`) .set('kbn-xsrf', 'foo'); if (this.user) { return request.auth(this.user.username, this.user.password); @@ -88,7 +88,7 @@ export class AlertUtils { public getMuteAllRequest(alertId: string) { const request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert/${alertId}/_mute_all`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}/_mute_all`) .set('kbn-xsrf', 'foo'); if (this.user) { return request.auth(this.user.username, this.user.password); @@ -98,7 +98,7 @@ export class AlertUtils { public getUnmuteAllRequest(alertId: string) { const request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert/${alertId}/_unmute_all`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}/_unmute_all`) .set('kbn-xsrf', 'foo'); if (this.user) { return request.auth(this.user.username, this.user.password); @@ -108,11 +108,7 @@ export class AlertUtils { public getMuteInstanceRequest(alertId: string, instanceId: string) { const request = this.supertestWithoutAuth - .post( - `${getUrlPrefix( - this.space.id - )}/api/alerts/alert/${alertId}/alert_instance/${instanceId}/_mute` - ) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}/alert/${instanceId}/_mute`) .set('kbn-xsrf', 'foo'); if (this.user) { return request.auth(this.user.username, this.user.password); @@ -123,9 +119,7 @@ export class AlertUtils { public getUnmuteInstanceRequest(alertId: string, instanceId: string) { const request = this.supertestWithoutAuth .post( - `${getUrlPrefix( - this.space.id - )}/api/alerts/alert/${alertId}/alert_instance/${instanceId}/_unmute` + `${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}/alert/${instanceId}/_unmute` ) .set('kbn-xsrf', 'foo'); if (this.user) { @@ -136,7 +130,7 @@ export class AlertUtils { public getUpdateApiKeyRequest(alertId: string) { const request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert/${alertId}/_update_api_key`) + .post(`${getUrlPrefix(this.space.id)}/internal/alerting/rule/${alertId}/_update_api_key`) .set('kbn-xsrf', 'foo'); if (this.user) { return request.auth(this.user.username, this.user.password); @@ -189,7 +183,7 @@ export class AlertUtils { } let request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo'); if (this.user) { request = request.auth(this.user.username, this.user.password); @@ -197,7 +191,7 @@ export class AlertUtils { const alertBody = getDefaultAlwaysFiringAlertData(reference, actionId); const response = await request.send({ ...alertBody, ...overwrites }); if (response.statusCode === 200) { - objRemover.add(this.space.id, response.body.id, 'alert', 'alerts'); + objRemover.add(this.space.id, response.body.id, 'rule', 'alerting'); } return response; } @@ -216,14 +210,16 @@ export class AlertUtils { } const request = this.supertestWithoutAuth - .put(`${getUrlPrefix(this.space.id)}/api/alerts/alert/${alertId}`) + .put(`${getUrlPrefix(this.space.id)}/api/alerting/rule/${alertId}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); - const { alertTypeId, enabled, consumer, ...alertBody } = getDefaultAlwaysFiringAlertData( - reference, - actionId - ); + const { + rule_type_id: alertTypeId, + enabled, + consumer, + ...alertBody + } = getDefaultAlwaysFiringAlertData(reference, actionId); const response = await request.send({ ...alertBody, ...overwrites }); return response; @@ -246,7 +242,7 @@ export class AlertUtils { } let request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo'); if (this.user) { request = request.auth(this.user.username, this.user.password); @@ -257,17 +253,18 @@ export class AlertUtils { schedule: { interval: '30s' }, throttle: '30s', tags: [], - alertTypeId: 'test.failing', + rule_type_id: 'test.failing', consumer: 'alertsFixture', params: { index: ES_TEST_INDEX_NAME, reference, }, + notify_when: 'onActiveAlert', actions: [], ...overwrites, }); if (response.statusCode === 200) { - objRemover.add(this.space.id, response.body.id, 'alert', 'alerts'); + objRemover.add(this.space.id, response.body.id, 'rule', 'alerting'); } return response; } @@ -290,7 +287,7 @@ export class AlertUtils { } let request = this.supertestWithoutAuth - .post(`${getUrlPrefix(this.space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(this.space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo'); if (this.user) { request = request.auth(this.user.username, this.user.password); @@ -300,7 +297,7 @@ export class AlertUtils { ...overwrites, }); if (response.statusCode === 200) { - objRemover.add(this.space.id, response.body.id, 'alert', 'alerts'); + objRemover.add(this.space.id, response.body.id, 'rule', 'alerting'); } return response; } @@ -339,12 +336,13 @@ instanceStateValue: {{state.instanceStateValue}} schedule: { interval: '1m' }, throttle: '1m', tags: ['tag-A', 'tag-B'], - alertTypeId: 'test.always-firing', + rule_type_id: 'test.always-firing', consumer: 'alertsFixture', params: { index: ES_TEST_INDEX_NAME, reference, }, + notify_when: 'onActiveAlert', actions: [ { group: 'default', diff --git a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts b/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts index 46624fcf86d37..22dc93b110a07 100644 --- a/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts +++ b/x-pack/test/alerting_api_integration/common/lib/get_test_alert_data.ts @@ -10,11 +10,11 @@ export function getTestAlertData(overwrites = {}) { enabled: true, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', schedule: { interval: '1m' }, throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', actions: [], params: {}, ...overwrites, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts index 2142520894669..1c227bc87d93f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts @@ -124,7 +124,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, createdAction.id, 'action', 'actions'); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts index 536c4cbbd710f..24e641cb06407 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/alerts.ts @@ -56,11 +56,11 @@ export default function alertTests({ getService }: FtrProviderContext) { before(async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My action', - actionTypeId: 'test.index-record', + connector_type_id: 'test.index-record', config: { unencrypted: `This value shouldn't get encrypted`, }, @@ -78,7 +78,7 @@ export default function alertTests({ getService }: FtrProviderContext) { objectRemover, }); }); - after(() => objectRemover.add(space.id, indexRecordActionId, 'action', 'actions')); + after(() => objectRemover.add(space.id, indexRecordActionId, 'connector', 'actions')); it('should schedule task, run alert and schedule actions when appropriate', async () => { const testStart = new Date(); @@ -365,24 +365,24 @@ instanceStateValue: true const retryDate = new Date(Date.now() + 60000); const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'Test rate limit', - actionTypeId: 'test.rate-limit', + connector_type_id: 'test.rate-limit', config: {}, }) .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.always-firing', + rule_type_id: 'test.always-firing', params: { index: ES_TEST_INDEX_NAME, reference: 'create-test-2', @@ -428,7 +428,7 @@ instanceStateValue: true case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); // Wait for the task to be attempted once and idle const scheduledActionTask = await retry.try(async () => { @@ -482,12 +482,12 @@ instanceStateValue: true const testStart = new Date(); const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.authorization', + rule_type_id: 'test.authorization', params: { callClusterAuthorizationIndex: authorizationIndex, savedObjectsClientType: 'dashboard', @@ -517,7 +517,7 @@ instanceStateValue: true case 'space_1_all_alerts_none_actions at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('alert:test.authorization', reference); @@ -548,7 +548,7 @@ instanceStateValue: true break; case 'superuser at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); // Wait for test.authorization to index a document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('alert:test.authorization', reference); @@ -581,21 +581,21 @@ instanceStateValue: true const testStart = new Date(); const reference = alertUtils.generateReference(); const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My action', - actionTypeId: 'test.authorization', + connector_type_id: 'test.authorization', }) .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.always-firing', + rule_type_id: 'test.always-firing', params: { index: ES_TEST_INDEX_NAME, reference, @@ -634,7 +634,7 @@ instanceStateValue: true case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('action:test.authorization', reference); @@ -673,7 +673,7 @@ instanceStateValue: true break; case 'superuser at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); // Ensure test.authorization indexed 1 document before disabling the alert and waiting for tasks to finish await esTestIndexTool.waitForDocs('action:test.authorization', reference); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index 842a3cc11c72c..70cafe407de29 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -40,18 +40,18 @@ export default function createAlertTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle create alert request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -93,7 +93,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'space_1_all at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); expect(response.body).to.eql({ id: response.body.id, name: 'abc', @@ -101,33 +101,35 @@ export default function createAlertTests({ getService }: FtrProviderContext) { actions: [ { id: createdAction.id, - actionTypeId: createdAction.actionTypeId, + connector_type_id: createdAction.connector_type_id, group: 'default', params: {}, }, ], enabled: true, - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', params: {}, - createdBy: user.username, + created_by: user.username, schedule: { interval: '1m' }, - scheduledTaskId: response.body.scheduledTaskId, - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, + scheduled_task_id: response.body.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, throttle: '1m', - notifyWhen: 'onThrottleInterval', - updatedBy: user.username, - apiKeyOwner: user.username, - muteAll: false, - mutedInstanceIds: [], - executionStatus: response.body.executionStatus, + notify_when: 'onThrottleInterval', + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + execution_status: response.body.execution_status, }); - expect(typeof response.body.scheduledTaskId).to.be('string'); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); + expect(typeof response.body.scheduled_task_id).to.be('string'); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); - const { _source: taskRecord } = await getScheduledTask(response.body.scheduledTaskId); + const { _source: taskRecord } = await getScheduledTask( + response.body.scheduled_task_id + ); expect(taskRecord.type).to.eql('task'); expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); expect(JSON.parse(taskRecord.task.params)).to.eql({ @@ -149,12 +151,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when consumer is the same as producer', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ); @@ -179,7 +181,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -188,11 +190,14 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when consumer is not the producer', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( - getTestAlertData({ alertTypeId: 'test.unrestricted-noop', consumer: 'alertsFixture' }) + getTestAlertData({ + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + }) ); switch (scenario.id) { @@ -226,7 +231,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -235,12 +240,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when consumer is "alerts"', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alerts', }) ); @@ -272,7 +277,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -281,12 +286,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when consumer is unknown', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'some consumer patrick invented', }) ); @@ -317,7 +322,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when an alert is disabled ', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(getTestAlertData({ enabled: false })); @@ -342,8 +347,8 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'space_1_all_alerts_none_actions at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); - expect(response.body.scheduledTaskId).to.eql(undefined); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); + expect(response.body.scheduled_task_id).to.eql(undefined); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -352,7 +357,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when alert name has leading and trailing whitespaces', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -382,7 +387,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); expect(response.body.name).to.eql(' leading and trailing whitespace '); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -391,12 +396,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when alert type is unregistered', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.unregistered-alert-type', + rule_type_id: 'test.unregistered-alert-type', }) ); @@ -422,7 +427,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when payload is empty and invalid', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({}); @@ -449,12 +454,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it(`should handle create alert request appropriately when params isn't valid`, async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( getTestAlertData({ - alertTypeId: 'test.validation', + rule_type_id: 'test.validation', }) ); @@ -492,7 +497,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when interval schedule is wrong syntax', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(getTestAlertData(getTestAlertData({ schedule: { interval: '10x' } }))); @@ -519,7 +524,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when interval schedule is 0', async () => { const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(getTestAlertData(getTestAlertData({ schedule: { interval: '0s' } }))); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts index 3734e4ebe7c8a..2cbb16ababd10 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/delete.ts @@ -40,13 +40,13 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle delete alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -64,9 +64,9 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { ), statusCode: 403, }); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'superuser at space1': case 'space_1_all at space1': @@ -75,7 +75,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -88,18 +88,18 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it('should handle delete alert request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -119,16 +119,16 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { ), statusCode: 403, }); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -141,15 +141,18 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it('should handle delete alert request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( - getTestAlertData({ alertTypeId: 'test.unrestricted-noop', consumer: 'alertsFixture' }) + getTestAlertData({ + rule_type_id: 'test.unrestricted-noop', + consumer: 'alertsFixture', + }) ) .expect(200); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -167,9 +170,9 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { ), statusCode: 403, }); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'space_1_all at space1': case 'space_1_all_alerts_none_actions at space1': @@ -183,16 +186,16 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { ), statusCode: 403, }); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -205,18 +208,18 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it('should handle delete alert request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alerts', }) ) .expect(200); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -241,9 +244,9 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { ), statusCode: 403, }); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'superuser at space1': case 'space_1_all at space1': @@ -252,7 +255,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -265,14 +268,14 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it(`shouldn't delete alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix('other')}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -298,7 +301,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it('should still be able to delete alert when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); @@ -318,7 +321,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { }); const response = await supertestWithoutAuth - .delete(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password); @@ -336,9 +339,9 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { ), statusCode: 403, }); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'superuser at space1': case 'space_1_all at space1': @@ -347,7 +350,7 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts index 17f4bdf61b220..b265451bbd632 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/disable.ts @@ -44,18 +44,18 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte describe(scenario.id, () => { it('should handle disable alert request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -70,7 +70,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getDisableRequest(createdAlert.id); @@ -89,7 +89,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte statusCode: 403, }); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'space_1_all_alerts_none_actions at space1': case 'superuser at space1': @@ -98,7 +98,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -118,17 +118,17 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it('should handle disable alert request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', enabled: true, }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getDisableRequest(createdAlert.id); @@ -154,7 +154,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -167,17 +167,17 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it('should handle disable alert request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', enabled: true, }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getDisableRequest(createdAlert.id); @@ -214,7 +214,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -227,17 +227,17 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it('should handle disable alert request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alerts', enabled: true, }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getDisableRequest(createdAlert.id); @@ -270,7 +270,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -283,11 +283,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it('should still be able to disable alert when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await retry.try(async () => { await supertest @@ -320,7 +320,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte statusCode: 403, }); // Ensure task still exists - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); break; case 'superuser at space1': case 'space_1_all at space1': @@ -329,7 +329,7 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -349,11 +349,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it(`shouldn't disable alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix('other')}/api/alerts/alert`) + .post(`${getUrlPrefix('other')}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add('other', createdAlert.id, 'alert', 'alerts'); + objectRemover.add('other', createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getDisableRequest(createdAlert.id); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts index 262a1a791a99c..70e286b795720 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/enable.ts @@ -44,18 +44,18 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex describe(scenario.id, () => { it('should handle enable alert request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -70,7 +70,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getEnableRequest(createdAlert.id); @@ -114,12 +114,14 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(typeof updatedAlert.scheduledTaskId).to.eql('string'); - const { _source: taskRecord } = await getScheduledTask(updatedAlert.scheduledTaskId); + expect(typeof updatedAlert.scheduled_task_id).to.eql('string'); + const { _source: taskRecord } = await getScheduledTask( + updatedAlert.scheduled_task_id + ); expect(taskRecord.type).to.eql('task'); expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); expect(JSON.parse(taskRecord.task.params)).to.eql({ @@ -141,17 +143,17 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it('should handle enable alert request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', enabled: false, }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getEnableRequest(createdAlert.id); @@ -177,7 +179,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -190,17 +192,17 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it('should handle enable alert request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', enabled: false, }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getEnableRequest(createdAlert.id); @@ -244,17 +246,17 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it('should handle enable alert request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alerts', enabled: false, }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getEnableRequest(createdAlert.id); @@ -287,7 +289,7 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); try { - await getScheduledTask(createdAlert.scheduledTaskId); + await getScheduledTask(createdAlert.scheduled_task_id); throw new Error('Should have removed scheduled task'); } catch (e) { expect(e.status).to.eql(404); @@ -300,11 +302,11 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it('should still be able to enable alert when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await retry.try(async () => { await supertest @@ -344,12 +346,14 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(typeof updatedAlert.scheduledTaskId).to.eql('string'); - const { _source: taskRecord } = await getScheduledTask(updatedAlert.scheduledTaskId); + expect(typeof updatedAlert.scheduled_task_id).to.eql('string'); + const { _source: taskRecord } = await getScheduledTask( + updatedAlert.scheduled_task_id + ); expect(taskRecord.type).to.eql('task'); expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); expect(JSON.parse(taskRecord.task.params)).to.eql({ @@ -371,11 +375,11 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it(`shouldn't enable alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix('other')}/api/alerts/alert`) + .post(`${getUrlPrefix('other')}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add('other', createdAlert.id, 'alert', 'alerts'); + objectRemover.add('other', createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getEnableRequest(createdAlert.id); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts index 2561a8f3a7d48..825ade55cb4b0 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/event_log.ts @@ -24,11 +24,11 @@ export default function eventLogTests({ getService }: FtrProviderContext) { it('should generate events for alert decrypt errors', async () => { const spaceId = Spaces[0].id; const response = await supertest - .post(`${getUrlPrefix(spaceId)}/api/alerts/alert`) + .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', schedule: { interval: '1s' }, throttle: null, }) @@ -36,7 +36,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { expect(response.status).to.eql(200); const alertId = response.body.id; - objectRemover.add(spaceId, alertId, 'alert', 'alerts'); + objectRemover.add(spaceId, alertId, 'rule', 'alerting'); await retry.try(async () => { // break AAD diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts index 18f0be353b7da..2bae1c541bc48 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts @@ -25,17 +25,17 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should eventually have error reason "decrypt" when appropriate', async () => { const response = await supertest - .post(`${getUrlPrefix(spaceId)}/api/alerts/alert`) + .post(`${getUrlPrefix(spaceId)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', schedule: { interval: '1s' }, }) ); expect(response.status).to.eql(200); const alertId = response.body.id; - objectRemover.add(spaceId, alertId, 'alert', 'alerts'); + objectRemover.add(spaceId, alertId, 'rule', 'alerting'); let executionStatus = await waitForStatus(alertId, new Set(['ok']), 10000); @@ -70,15 +70,15 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon expect().fail(`waiting for alert ${id} statuses ${Array.from(statuses)} timed out`); } - const response = await supertest.get(`${getUrlPrefix(spaceId)}/api/alerts/alert/${id}`); + const response = await supertest.get(`${getUrlPrefix(spaceId)}/api/alerting/rule/${id}`); expect(response.status).to.eql(200); - const { status } = response.body.executionStatus; - if (statuses.has(status)) return response.body.executionStatus; + const { status } = response.body.execution_status; + if (statuses.has(status)) return response.body.execution_status; // eslint-disable-next-line no-console console.log( `waitForStatus(${Array.from(statuses)}): got ${JSON.stringify( - response.body.executionStatus + response.body.execution_status )}, retrying` ); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index 29a5ef6fdf186..dda5970904f8d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -27,17 +27,17 @@ export default function createFindTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle find alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth .get( `${getUrlPrefix( space.id - )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ) .auth(user.username, user.password); @@ -58,33 +58,33 @@ export default function createFindTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); expect(response.body.page).to.equal(1); - expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); expect(match).to.eql({ id: createdAlert.id, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', schedule: { interval: '1m' }, enabled: true, actions: [], params: {}, - createdBy: 'elastic', - scheduledTaskId: match.scheduledTaskId, - createdAt: match.createdAt, - updatedAt: match.updatedAt, + created_by: 'elastic', + scheduled_task_id: match.scheduled_task_id, + created_at: match.created_at, + updated_at: match.updated_at, throttle: '1m', - notifyWhen: 'onThrottleInterval', - updatedBy: 'elastic', - apiKeyOwner: 'elastic', - muteAll: false, - mutedInstanceIds: [], - executionStatus: match.executionStatus, + notify_when: 'onThrottleInterval', + updated_by: 'elastic', + api_key_owner: 'elastic', + mute_all: false, + muted_alert_ids: [], + execution_status: match.execution_status, }); - expect(Date.parse(match.createdAt)).to.be.greaterThan(0); - expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); + expect(Date.parse(match.created_at)).to.be.greaterThan(0); + expect(Date.parse(match.updated_at)).to.be.greaterThan(0); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -95,25 +95,25 @@ export default function createFindTests({ getService }: FtrProviderContext) { async function createNoOpAlert(overrides = {}) { const alert = getTestAlertData(overrides); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(alert) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); return { id: createdAlert.id, - alertTypeId: alert.alertTypeId, + rule_type_id: alert.rule_type_id, }; } function createRestrictedNoOpAlert() { return createNoOpAlert({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }); } function createUnrestrictedNoOpAlert() { return createNoOpAlert({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }); } @@ -131,7 +131,9 @@ export default function createFindTests({ getService }: FtrProviderContext) { const response = await supertestWithoutAuth .get( - `${getUrlPrefix(space.id)}/api/alerts/_find?per_page=${perPage}&sort_field=createdAt` + `${getUrlPrefix( + space.id + )}/api/alerting/rules/_find?per_page=${perPage}&sort_field=createdAt` ) .auth(user.username, user.password); @@ -149,12 +151,12 @@ export default function createFindTests({ getService }: FtrProviderContext) { case 'space_1_all_alerts_none_actions at space1': expect(response.statusCode).to.eql(200); expect(response.body.page).to.equal(1); - expect(response.body.perPage).to.be.equal(perPage); + expect(response.body.per_page).to.be.equal(perPage); expect(response.body.total).to.be.equal(6); { const [firstPage] = chunk( allAlerts - .filter((alert) => alert.alertTypeId !== 'test.restricted-noop') + .filter((alert) => alert.rule_type_id !== 'test.restricted-noop') .map((alert) => alert.id), perPage ); @@ -166,7 +168,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); expect(response.body.page).to.equal(1); - expect(response.body.perPage).to.be.equal(perPage); + expect(response.body.per_page).to.be.equal(perPage); expect(response.body.total).to.be.equal(8); { @@ -180,7 +182,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { .get( `${getUrlPrefix( space.id - )}/api/alerts/_find?per_page=${perPage}&sort_field=createdAt&page=2` + )}/api/alerting/rules/_find?per_page=${perPage}&sort_field=createdAt&page=2` ) .auth(user.username, user.password); @@ -195,18 +197,18 @@ export default function createFindTests({ getService }: FtrProviderContext) { it('should handle find alert request with filter appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -221,13 +223,13 @@ export default function createFindTests({ getService }: FtrProviderContext) { }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth .get( `${getUrlPrefix( space.id - )}/api/alerts/_find?filter=alert.attributes.actions:{ actionTypeId: test.noop }` + )}/api/alerting/rules/_find?filter=alert.attributes.actions:{ actionTypeId: test.noop }` ) .auth(user.username, user.password); @@ -248,14 +250,14 @@ export default function createFindTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); expect(response.body.page).to.equal(1); - expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); expect(match).to.eql({ id: createdAlert.id, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', schedule: { interval: '1m' }, enabled: false, @@ -263,24 +265,24 @@ export default function createFindTests({ getService }: FtrProviderContext) { { id: createdAction.id, group: 'default', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', params: {}, }, ], params: {}, - createdBy: 'elastic', + created_by: 'elastic', throttle: '1m', - updatedBy: 'elastic', - apiKeyOwner: null, - muteAll: false, - mutedInstanceIds: [], - notifyWhen: 'onThrottleInterval', - createdAt: match.createdAt, - updatedAt: match.updatedAt, - executionStatus: match.executionStatus, + updated_by: 'elastic', + api_key_owner: null, + mute_all: false, + muted_alert_ids: [], + notify_when: 'onThrottleInterval', + created_at: match.created_at, + updated_at: match.updated_at, + execution_status: match.execution_status, }); - expect(Date.parse(match.createdAt)).to.be.greaterThan(0); - expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); + expect(Date.parse(match.created_at)).to.be.greaterThan(0); + expect(Date.parse(match.updated_at)).to.be.greaterThan(0); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -290,38 +292,38 @@ export default function createFindTests({ getService }: FtrProviderContext) { it('should handle find alert request with fields appropriately', async () => { const myTag = uuid.v4(); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, tags: [myTag], - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // create another type with same tag const { body: createdSecondAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ tags: [myTag], - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdSecondAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdSecondAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth .get( `${getUrlPrefix( space.id - )}/api/alerts/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags"]&sort_field=createdAt` + )}/api/alerting/rules/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags"]&sort_field=createdAt` ) .auth(user.username, user.password); @@ -345,7 +347,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); expect(response.body.page).to.equal(1); - expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const [matchFirst, matchSecond] = response.body.data; expect(omit(matchFirst, 'updatedAt')).to.eql({ @@ -367,38 +369,38 @@ export default function createFindTests({ getService }: FtrProviderContext) { it('should handle find alert request with executionStatus field appropriately', async () => { const myTag = uuid.v4(); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, tags: [myTag], - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); // create another type with same tag const { body: createdSecondAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ tags: [myTag], - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdSecondAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdSecondAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth .get( `${getUrlPrefix( space.id - )}/api/alerts/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags","executionStatus"]&sort_field=createdAt` + )}/api/alerting/rules/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags","executionStatus"]&sort_field=createdAt` ) .auth(user.username, user.password); @@ -422,20 +424,20 @@ export default function createFindTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); expect(response.body.page).to.equal(1); - expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const [matchFirst, matchSecond] = response.body.data; expect(omit(matchFirst, 'updatedAt')).to.eql({ id: createdAlert.id, actions: [], tags: [myTag], - executionStatus: matchFirst.executionStatus, + execution_status: matchFirst.execution_status, }); expect(omit(matchSecond, 'updatedAt')).to.eql({ id: createdSecondAlert.id, actions: [], tags: [myTag], - executionStatus: matchSecond.executionStatus, + execution_status: matchSecond.execution_status, }); break; default: @@ -445,15 +447,17 @@ export default function createFindTests({ getService }: FtrProviderContext) { it(`shouldn't find alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth .get( - `${getUrlPrefix('other')}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` + `${getUrlPrefix( + 'other' + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ) .auth(user.username, user.password); @@ -475,7 +479,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(200); expect(response.body).to.eql({ page: 1, - perPage: 10, + per_page: 10, total: 0, data: [], }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts index 1277084b9eb0d..0400557209348 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts @@ -31,14 +31,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle get alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .auth(user.username, user.password); switch (scenario.id) { @@ -61,26 +61,26 @@ export default function createGetTests({ getService }: FtrProviderContext) { id: createdAlert.id, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', schedule: { interval: '1m' }, enabled: true, actions: [], params: {}, - createdBy: 'elastic', - scheduledTaskId: response.body.scheduledTaskId, - updatedAt: response.body.updatedAt, - createdAt: response.body.createdAt, + created_by: 'elastic', + scheduled_task_id: response.body.scheduled_task_id, + updated_at: response.body.updated_at, + created_at: response.body.created_at, throttle: '1m', - notifyWhen: 'onThrottleInterval', - updatedBy: 'elastic', - apiKeyOwner: 'elastic', - muteAll: false, - mutedInstanceIds: [], - executionStatus: response.body.executionStatus, + notify_when: 'onThrottleInterval', + updated_by: 'elastic', + api_key_owner: 'elastic', + mute_all: false, + muted_alert_ids: [], + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -89,19 +89,19 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('should handle get alert request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .auth(user.username, user.password); switch (scenario.id) { @@ -132,19 +132,19 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('should handle get alert request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .auth(user.username, user.password); switch (scenario.id) { @@ -186,19 +186,19 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('should handle get alert request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .auth(user.username, user.password); switch (scenario.id) { @@ -240,14 +240,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { it(`shouldn't get alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix('other')}/api/alerting/rule/${createdAlert.id}`) .auth(user.username, user.password); expect(response.statusCode).to.eql(404); @@ -272,7 +272,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it(`should handle get alert request appropriately when alert doesn't exist`, async () => { const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/1`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) .auth(user.username, user.password); switch (scenario.id) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_instance_summary.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_instance_summary.ts index 11514a8cf630c..d4ca6c2aa9cd8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_instance_summary.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_instance_summary.ts @@ -32,14 +32,16 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr describe(scenario.id, () => { it('should handle getAlertInstanceSummary alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary`) + .get( + `${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}/_alert_summary` + ) .auth(user.username, user.password); switch (scenario.id) { @@ -58,27 +60,31 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr case 'space_1_all_alerts_none_actions at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - const { id, statusStartDate, statusEndDate } = response.body; + const { + id, + status_start_date: statusStartDate, + status_end_date: statusEndDate, + } = response.body; expect(id).to.equal(createdAlert.id); expect(Date.parse(statusStartDate)).to.be.lessThan(Date.parse(statusEndDate)); const stableBody = omit(response.body, [ 'id', - 'statusStartDate', - 'statusEndDate', - 'lastRun', + 'status_start_date', + 'status_end_date', + 'last_run', ]); expect(stableBody).to.eql({ name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', - muteAll: false, + mute_all: false, throttle: '1m', enabled: true, - errorMessages: [], - instances: {}, + error_messages: [], + alerts: {}, }); break; default: @@ -88,19 +94,21 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr it('should handle getAlertInstanceSummary alert request appropriately when unauthorized', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary`) + .get( + `${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}/_alert_summary` + ) .auth(user.username, user.password); switch (scenario.id) { @@ -134,7 +142,7 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - expect(response.body).to.key('id', 'instances', 'errorMessages'); + expect(response.body).to.key('id', 'alerts', 'error_messages'); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -143,14 +151,16 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr it(`shouldn't getAlertInstanceSummary for an alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}/_instance_summary`) + .get( + `${getUrlPrefix('other')}/internal/alerting/rule/${createdAlert.id}/_alert_summary` + ) .auth(user.username, user.password); expect(response.statusCode).to.eql(404); @@ -175,7 +185,7 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr it(`should handle getAlertInstanceSummary request appropriately when alert doesn't exist`, async () => { const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/1/_instance_summary`) + .get(`${getUrlPrefix(space.id)}/internal/alerting/rule/1/_alert_summary`) .auth(user.username, user.password); switch (scenario.id) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts index 5dcecd3f1b05f..e00d8e53e438e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get_alert_state.ts @@ -31,14 +31,14 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont describe(scenario.id, () => { it('should handle getAlertState alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/state`) + .get(`${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}/state`) .auth(user.username, user.password); switch (scenario.id) { @@ -57,7 +57,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont case 'space_1_all_alerts_none_actions at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - expect(response.body).to.key('alertInstances', 'previousStartedAt'); + expect(response.body).to.key('alerts', 'previous_started_at'); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -66,19 +66,19 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it('should handle getAlertState alert request appropriately when unauthorized', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/state`) + .get(`${getUrlPrefix(space.id)}/internal/alerting/rule/${createdAlert.id}/state`) .auth(user.username, user.password); switch (scenario.id) { @@ -112,7 +112,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont case 'superuser at space1': case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); - expect(response.body).to.key('alertInstances', 'previousStartedAt'); + expect(response.body).to.key('alerts', 'previous_started_at'); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); @@ -121,14 +121,14 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it(`shouldn't getAlertState for an alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .get(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}/state`) + .get(`${getUrlPrefix('other')}/internal/alerting/rule/${createdAlert.id}/state`) .auth(user.username, user.password); expect(response.statusCode).to.eql(404); @@ -153,7 +153,7 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it(`should handle getAlertState request appropriately when alert doesn't exist`, async () => { const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/1/state`) + .get(`${getUrlPrefix(space.id)}/internal/alerting/rule/1/state`) .auth(user.username, user.password); switch (scenario.id) { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts index e8cc8ea699e17..b1b52d89997cd 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts @@ -41,7 +41,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./get_alert_state')); loadTestFile(require.resolve('./get_alert_instance_summary')); - loadTestFile(require.resolve('./list_alert_types')); + loadTestFile(require.resolve('./rule_types')); loadTestFile(require.resolve('./mute_all')); loadTestFile(require.resolve('./mute_instance')); loadTestFile(require.resolve('./unmute_all')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts index 6ac50d8485728..8344d4a281ba1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mustache_templates.ts @@ -62,28 +62,28 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should render kibanaBaseUrl as non-empty string since configured', async () => { const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces[0].id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces[0].id)}/api/actions/connector`) .set('kbn-xsrf', 'test') .send({ name: 'testing context variable expansion', - actionTypeId: '.slack', + connector_type_id: '.slack', secrets: { webhookUrl: slackSimulatorURL, }, }); expect(actionResponse.status).to.eql(200); const createdAction = actionResponse.body; - objectRemover.add(Spaces[0].id, createdAction.id, 'action', 'actions'); + objectRemover.add(Spaces[0].id, createdAction.id, 'connector', 'actions'); const varsTemplate = 'kibanaBaseUrl: "{{kibanaBaseUrl}}"'; const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces[0].id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces[0].id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ name: 'testing context variable kibanaBaseUrl', - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern: { instance: [true, true] }, }, @@ -100,7 +100,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(alertResponse.status).to.eql(200); const createdAlert = alertResponse.body; - objectRemover.add(Spaces[0].id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces[0].id, createdAlert.id, 'rule', 'alerting'); const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, createdAlert.id) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts index f226537e462f1..993b66353756f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_all.ts @@ -35,18 +35,18 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) describe(scenario.id, () => { it('should handle mute alert request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -61,7 +61,7 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteAllRequest(createdAlert.id); @@ -94,11 +94,11 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(true); + expect(updatedAlert.mute_all).to.eql(true); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -114,17 +114,17 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) it('should handle mute alert request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteAllRequest(createdAlert.id); @@ -150,11 +150,11 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(true); + expect(updatedAlert.mute_all).to.eql(true); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -170,17 +170,17 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) it('should handle mute alert request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteAllRequest(createdAlert.id); @@ -217,11 +217,11 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(true); + expect(updatedAlert.mute_all).to.eql(true); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -237,17 +237,17 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) it('should handle mute alert request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteAllRequest(createdAlert.id); @@ -284,11 +284,11 @@ export default function createMuteAlertTests({ getService }: FtrProviderContext) expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(true); + expect(updatedAlert.mute_all).to.eql(true); // Ensure AAD isn't broken await checkAAD({ supertest, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts index f70a2a1af5429..858e61154cef5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/mute_instance.ts @@ -35,18 +35,18 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider describe(scenario.id, () => { it('should handle mute alert instance request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -61,7 +61,7 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteInstanceRequest(createdAlert.id, '1'); @@ -94,11 +94,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql(['1']); + expect(updatedAlert.muted_alert_ids).to.eql(['1']); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -114,17 +114,17 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle mute alert instance request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteInstanceRequest(createdAlert.id, '1'); @@ -150,11 +150,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql(['1']); + expect(updatedAlert.muted_alert_ids).to.eql(['1']); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -170,17 +170,17 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle mute alert instance request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteInstanceRequest(createdAlert.id, '1'); @@ -217,11 +217,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql(['1']); + expect(updatedAlert.muted_alert_ids).to.eql(['1']); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -237,17 +237,17 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle mute alert instance request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getMuteInstanceRequest(createdAlert.id, '1'); @@ -284,11 +284,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql(['1']); + expect(updatedAlert.muted_alert_ids).to.eql(['1']); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -304,16 +304,14 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle mute alert instance request appropriately and not duplicate mutedInstanceIds when muting an instance already muted', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post( - `${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/alert_instance/1/_mute` - ) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/alert/1/_mute`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -341,11 +339,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql(['1']); + expect(updatedAlert.muted_alert_ids).to.eql(['1']); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts index 3db3565374740..fb32be12500ca 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rbac_legacy.ts @@ -160,7 +160,7 @@ export default function alertTests({ getService }: FtrProviderContext) { async function ensureLegacyAlertHasBeenMigrated(alertId: string) { const getResponse = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${alertId}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${alertId}`) .auth(user.username, user.password); expect(getResponse.status).to.eql(200); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts similarity index 66% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts index 1da93e2d850a3..c851aaf2bbc88 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/list_alert_types.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/rule_types.ts @@ -16,56 +16,56 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const expectedNoOpType = { - actionGroups: [ + action_groups: [ { id: 'default', name: 'Default' }, { id: 'recovered', name: 'Recovered' }, ], - defaultActionGroupId: 'default', + default_action_group_id: 'default', id: 'test.noop', name: 'Test: Noop', - actionVariables: { + action_variables: { state: [], context: [], params: [], }, producer: 'alertsFixture', - minimumLicenseRequired: 'basic', - recoveryActionGroup: { + minimum_license_required: 'basic', + recovery_action_group: { id: 'recovered', name: 'Recovered', }, - enabledInLicense: true, + enabled_in_license: true, }; const expectedRestrictedNoOpType = { - actionGroups: [ + action_groups: [ { id: 'default', name: 'Default' }, { id: 'restrictedRecovered', name: 'Restricted Recovery' }, ], - recoveryActionGroup: { + recovery_action_group: { id: 'restrictedRecovered', name: 'Restricted Recovery', }, - defaultActionGroupId: 'default', + default_action_group_id: 'default', id: 'test.restricted-noop', name: 'Test: Restricted Noop', - actionVariables: { + action_variables: { state: [], context: [], params: [], }, producer: 'alertsRestrictedFixture', - minimumLicenseRequired: 'basic', - enabledInLicense: true, + minimum_license_required: 'basic', + enabled_in_license: true, }; - describe('list_alert_types', () => { + describe('rule_types', () => { for (const scenario of UserAtSpaceScenarios) { const { user, space } = scenario; describe(scenario.id, () => { it('should return 200 with list of globally available alert types', async () => { const response = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/list_alert_types`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule_types`) .auth(user.username, user.password); expect(response.statusCode).to.eql(200); @@ -82,76 +82,76 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { break; case 'space_1_all at space1': case 'space_1_all_alerts_none_actions at space1': - expect(omit(noOpAlertType, 'authorizedConsumers')).to.eql(expectedNoOpType); + expect(omit(noOpAlertType, 'authorized_consumers')).to.eql(expectedNoOpType); expect(restrictedNoOpAlertType).to.eql(undefined); - expect(noOpAlertType.authorizedConsumers).to.eql({ + expect(noOpAlertType.authorized_consumers).to.eql({ alerts: { read: true, all: true }, alertsFixture: { read: true, all: true }, }); break; case 'global_read at space1': - expect(omit(noOpAlertType, 'authorizedConsumers')).to.eql(expectedNoOpType); - expect(noOpAlertType.authorizedConsumers.alertsFixture).to.eql({ + expect(omit(noOpAlertType, 'authorized_consumers')).to.eql(expectedNoOpType); + expect(noOpAlertType.authorized_consumers.alertsFixture).to.eql({ read: true, all: false, }); - expect(noOpAlertType.authorizedConsumers.alertsRestrictedFixture).to.eql({ + expect(noOpAlertType.authorized_consumers.alertsRestrictedFixture).to.eql({ read: true, all: false, }); - expect(omit(restrictedNoOpAlertType, 'authorizedConsumers')).to.eql( + expect(omit(restrictedNoOpAlertType, 'authorized_consumers')).to.eql( expectedRestrictedNoOpType ); - expect(Object.keys(restrictedNoOpAlertType.authorizedConsumers)).not.to.contain( + expect(Object.keys(restrictedNoOpAlertType.authorized_consumers)).not.to.contain( 'alertsFixture' ); - expect(restrictedNoOpAlertType.authorizedConsumers.alertsRestrictedFixture).to.eql({ + expect(restrictedNoOpAlertType.authorized_consumers.alertsRestrictedFixture).to.eql({ read: true, all: false, }); break; case 'space_1_all_with_restricted_fixture at space1': - expect(omit(noOpAlertType, 'authorizedConsumers')).to.eql(expectedNoOpType); - expect(noOpAlertType.authorizedConsumers.alertsFixture).to.eql({ + expect(omit(noOpAlertType, 'authorized_consumers')).to.eql(expectedNoOpType); + expect(noOpAlertType.authorized_consumers.alertsFixture).to.eql({ read: true, all: true, }); - expect(noOpAlertType.authorizedConsumers.alertsRestrictedFixture).to.eql({ + expect(noOpAlertType.authorized_consumers.alertsRestrictedFixture).to.eql({ read: true, all: true, }); - expect(omit(restrictedNoOpAlertType, 'authorizedConsumers')).to.eql( + expect(omit(restrictedNoOpAlertType, 'authorized_consumers')).to.eql( expectedRestrictedNoOpType ); - expect(Object.keys(restrictedNoOpAlertType.authorizedConsumers)).not.to.contain( + expect(Object.keys(restrictedNoOpAlertType.authorized_consumers)).not.to.contain( 'alertsFixture' ); - expect(restrictedNoOpAlertType.authorizedConsumers.alertsRestrictedFixture).to.eql({ + expect(restrictedNoOpAlertType.authorized_consumers.alertsRestrictedFixture).to.eql({ read: true, all: true, }); break; case 'superuser at space1': - expect(omit(noOpAlertType, 'authorizedConsumers')).to.eql(expectedNoOpType); - expect(noOpAlertType.authorizedConsumers.alertsFixture).to.eql({ + expect(omit(noOpAlertType, 'authorized_consumers')).to.eql(expectedNoOpType); + expect(noOpAlertType.authorized_consumers.alertsFixture).to.eql({ read: true, all: true, }); - expect(noOpAlertType.authorizedConsumers.alertsRestrictedFixture).to.eql({ + expect(noOpAlertType.authorized_consumers.alertsRestrictedFixture).to.eql({ read: true, all: true, }); - expect(omit(restrictedNoOpAlertType, 'authorizedConsumers')).to.eql( + expect(omit(restrictedNoOpAlertType, 'authorized_consumers')).to.eql( expectedRestrictedNoOpType ); - expect(noOpAlertType.authorizedConsumers.alertsFixture).to.eql({ + expect(noOpAlertType.authorized_consumers.alertsFixture).to.eql({ read: true, all: true, }); - expect(noOpAlertType.authorizedConsumers.alertsRestrictedFixture).to.eql({ + expect(noOpAlertType.authorized_consumers.alertsRestrictedFixture).to.eql({ read: true, all: true, }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts index 885759359f229..526f809033646 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_all.ts @@ -35,18 +35,18 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex describe(scenario.id, () => { it('should handle unmute alert request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -61,10 +61,10 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/_mute_all`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/_mute_all`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -99,11 +99,11 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(false); + expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -119,20 +119,20 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex it('should handle unmute alert request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/_mute_all`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/_mute_all`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -160,11 +160,11 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(false); + expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -180,20 +180,20 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex it('should handle unmute alert request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/_mute_all`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/_mute_all`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -232,11 +232,11 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(false); + expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -252,20 +252,20 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex it('should handle unmute alert request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/_mute_all`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/_mute_all`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -304,11 +304,11 @@ export default function createUnmuteAlertTests({ getService }: FtrProviderContex expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.muteAll).to.eql(false); + expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ supertest, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts index fed008468e735..c19da8ba2008c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/unmute_instance.ts @@ -35,18 +35,18 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider describe(scenario.id, () => { it('should handle unmute alert instance request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -61,12 +61,10 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post( - `${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/alert_instance/1/_mute` - ) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/alert/1/_mute`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -101,11 +99,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql([]); + expect(updatedAlert.muted_alert_ids).to.eql([]); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -121,22 +119,20 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle unmute alert instance request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post( - `${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/alert_instance/1/_mute` - ) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/alert/1/_mute`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -164,11 +160,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql([]); + expect(updatedAlert.muted_alert_ids).to.eql([]); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -184,22 +180,20 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle unmute alert instance request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post( - `${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/alert_instance/1/_mute` - ) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/alert/1/_mute`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -238,11 +232,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql([]); + expect(updatedAlert.muted_alert_ids).to.eql([]); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -258,22 +252,20 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider it('should handle unmute alert instance request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ enabled: false, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await supertest - .post( - `${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}/alert_instance/1/_mute` - ) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}/alert/1/_mute`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -312,11 +304,11 @@ export default function createMuteAlertInstanceTests({ getService }: FtrProvider expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql([]); + expect(updatedAlert.muted_alert_ids).to.eql([]); // Ensure AAD isn't broken await checkAAD({ supertest, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index 300a884d5a006..e628f0b3d950e 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -42,22 +42,22 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { describe(scenario.id, () => { it('should handle update alert request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const updatedData = { name: 'bcd', @@ -74,10 +74,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }, ], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -112,31 +112,31 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ ...updatedData, id: createdAlert.id, - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', - createdBy: 'elastic', + created_by: 'elastic', enabled: true, - updatedBy: user.username, - apiKeyOwner: user.username, - muteAll: false, - mutedInstanceIds: [], + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], actions: [ { id: createdAction.id, - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', group: 'default', params: {}, }, ], - scheduledTaskId: createdAlert.scheduledTaskId, - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan( - Date.parse(response.body.createdAt) + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) ); // Ensure AAD isn't broken await checkAAD({ @@ -153,16 +153,16 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const updatedData = { name: 'bcd', @@ -173,10 +173,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -204,23 +204,23 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ ...updatedData, id: createdAlert.id, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', - createdBy: 'elastic', + created_by: 'elastic', enabled: true, - updatedBy: user.username, - apiKeyOwner: user.username, - muteAll: false, - mutedInstanceIds: [], - scheduledTaskId: createdAlert.scheduledTaskId, - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan( - Date.parse(response.body.createdAt) + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) ); // Ensure AAD isn't broken await checkAAD({ @@ -237,16 +237,16 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const updatedData = { name: 'bcd', @@ -257,10 +257,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -299,23 +299,23 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ ...updatedData, id: createdAlert.id, - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', - createdBy: 'elastic', + created_by: 'elastic', enabled: true, - updatedBy: user.username, - apiKeyOwner: user.username, - muteAll: false, - mutedInstanceIds: [], - scheduledTaskId: createdAlert.scheduledTaskId, - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan( - Date.parse(response.body.createdAt) + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) ); // Ensure AAD isn't broken await checkAAD({ @@ -332,16 +332,16 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const updatedData = { name: 'bcd', @@ -352,10 +352,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -394,23 +394,23 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ ...updatedData, id: createdAlert.id, - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', - createdBy: 'elastic', + created_by: 'elastic', enabled: true, - updatedBy: user.username, - apiKeyOwner: user.username, - muteAll: false, - mutedInstanceIds: [], - scheduledTaskId: createdAlert.scheduledTaskId, - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan( - Date.parse(response.body.createdAt) + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) ); // Ensure AAD isn't broken await checkAAD({ @@ -427,11 +427,11 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should still be able to update when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await retry.try(async () => { await supertest @@ -456,10 +456,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -487,23 +487,23 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ ...updatedData, id: createdAlert.id, - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', - createdBy: 'elastic', + created_by: 'elastic', enabled: true, - updatedBy: user.username, - apiKeyOwner: user.username, - muteAll: false, - mutedInstanceIds: [], - scheduledTaskId: createdAlert.scheduledTaskId, - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + updated_by: user.username, + api_key_owner: user.username, + mute_all: false, + muted_alert_ids: [], + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan( - Date.parse(response.body.createdAt) + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) ); // Ensure AAD isn't broken await checkAAD({ @@ -520,11 +520,11 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when alert name has leading and trailing whitespaces', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const updatedData = { name: ' leading and trailing whitespace ', @@ -535,10 +535,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onActiveAlert', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -572,14 +572,14 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it(`shouldn't update alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .put(`${getUrlPrefix('other')}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix('other')}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ @@ -591,6 +591,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, throttle: '1m', actions: [], + notify_when: 'onActiveAlert', }); expect(response.statusCode).to.eql(404); @@ -615,26 +616,27 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when attempting to change alert type', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ name: 'bcd', tags: ['bar'], throttle: '1m', - alertTypeId: '1', + rule_type_id: '1', params: { foo: true, }, schedule: { interval: '12s' }, actions: [], + notify_when: 'onActiveAlert', }); switch (scenario.id) { @@ -649,7 +651,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { expect(response.body).to.eql({ statusCode: 400, error: 'Bad Request', - message: '[request body.alertTypeId]: definition for this key is missing', + message: '[request body.rule_type_id]: definition for this key is missing', }); break; default: @@ -659,7 +661,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when payload is empty and invalid', async () => { const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/1`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({}); @@ -686,21 +688,21 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it(`should handle update alert request appropriately when alertTypeConfig isn't valid`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.validation', + rule_type_id: 'test.validation', params: { param1: 'test', }, }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send({ @@ -710,6 +712,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { throttle: '1m', params: {}, actions: [], + notify_when: 'onActiveAlert', }); switch (scenario.id) { @@ -746,7 +749,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately when interval schedule is wrong syntax', async () => { const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/1`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/1`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send( @@ -779,7 +782,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle updates to an alert schedule by rescheduling the underlying task', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -787,10 +790,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduledTaskId)).docs[0]; + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; expect(alertTask.status).to.eql('idle'); // ensure the alert inital run has completed and it's been rescheduled to half an hour from now ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 30 * 60 * 1000); @@ -805,10 +808,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '1m' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -834,7 +837,8 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduledTaskId)).docs[0]; + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) + .docs[0]; expect(alertTask.status).to.eql('idle'); // ensure the alert is rescheduled to a minute from now ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 60 * 1000); @@ -847,21 +851,22 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle updates for a long running alert type without failing the underlying tasks due to invalidated ApiKey', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send({ enabled: true, name: 'abc', tags: ['foo'], - alertTypeId: 'test.longRunning', + rule_type_id: 'test.longRunning', consumer: 'alertsFixture', schedule: { interval: '1s' }, throttle: '1m', actions: [], params: {}, + notify_when: 'onThrottleInterval', }) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const updatedData = { name: 'bcd', tags: ['bar'], @@ -871,17 +876,17 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '1m' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); const statusUpdates: string[] = []; await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduledTaskId)).docs[0]; + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; statusUpdates.push(alertTask.status); expect(alertTask.status).to.eql('idle'); }); @@ -909,7 +914,8 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduledTaskId)).docs[0]; + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) + .docs[0]; expect(alertTask.status).to.eql('idle'); // ensure the alert is rescheduled to a minute from now ensureDatetimeIsWithinRange(Date.parse(alertTask.runAt), 60 * 1000); @@ -922,7 +928,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle updates to an alert schedule by setting the new schedule for the underlying task', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -930,10 +936,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduledTaskId)).docs[0]; + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)).docs[0]; expect(alertTask.status).to.eql('idle'); expect(alertTask.schedule).to.eql({ interval: '1m' }); }); @@ -947,10 +953,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '1s' }, actions: [], throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', }; const response = await supertestWithoutAuth - .put(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .send(updatedData); @@ -976,7 +982,8 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { case 'space_1_all_with_restricted_fixture at space1': expect(response.statusCode).to.eql(200); await retry.try(async () => { - const alertTask = (await getAlertingTaskById(createdAlert.scheduledTaskId)).docs[0]; + const alertTask = (await getAlertingTaskById(createdAlert.scheduled_task_id)) + .docs[0]; expect(alertTask.status).to.eql('idle'); expect(alertTask.schedule).to.eql({ interval: '1s' }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts index d8e07812286a9..a434109a18933 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update_api_key.ts @@ -36,18 +36,18 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte describe(scenario.id, () => { it('should handle update alert api key request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -61,7 +61,7 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getUpdateApiKeyRequest(createdAlert.id); switch (scenario.id) { @@ -93,11 +93,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.apiKeyOwner).to.eql(user.username); + expect(updatedAlert.api_key_owner).to.eql(user.username); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -113,16 +113,16 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it('should handle update alert api key request appropriately when consumer is the same as producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alertsRestrictedFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getUpdateApiKeyRequest(createdAlert.id); switch (scenario.id) { @@ -147,11 +147,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.apiKeyOwner).to.eql(user.username); + expect(updatedAlert.api_key_owner).to.eql(user.username); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -167,16 +167,16 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it('should handle update alert api key request appropriately when consumer is not the producer', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.unrestricted-noop', + rule_type_id: 'test.unrestricted-noop', consumer: 'alertsFixture', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getUpdateApiKeyRequest(createdAlert.id); switch (scenario.id) { @@ -212,11 +212,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.apiKeyOwner).to.eql(user.username); + expect(updatedAlert.api_key_owner).to.eql(user.username); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -232,16 +232,16 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it('should handle update alert api key request appropriately when consumer is "alerts"', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.restricted-noop', + rule_type_id: 'test.restricted-noop', consumer: 'alerts', }) ) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getUpdateApiKeyRequest(createdAlert.id); switch (scenario.id) { @@ -277,11 +277,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.apiKeyOwner).to.eql(user.username); + expect(updatedAlert.api_key_owner).to.eql(user.username); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -297,11 +297,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it('should still be able to update API key when AAD is broken', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(space.id, createdAlert.id, 'rule', 'alerting'); await retry.try(async () => { await supertest @@ -341,11 +341,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte expect(response.statusCode).to.eql(204); expect(response.body).to.eql(''); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(space.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(space.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .auth(user.username, user.password) .expect(200); - expect(updatedAlert.apiKeyOwner).to.eql(user.username); + expect(updatedAlert.api_key_owner).to.eql(user.username); // Ensure AAD isn't broken await checkAAD({ supertest, @@ -361,11 +361,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it(`shouldn't update alert api key from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix('other')}/api/alerts/alert`) + .post(`${getUrlPrefix('other')}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add('other', createdAlert.id, 'alert', 'alerts'); + objectRemover.add('other', createdAlert.id, 'rule', 'alerting'); const response = await alertUtils.getUpdateApiKeyRequest(createdAlert.id); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts index 140812bf2032a..65aa38cb23c24 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/aggregate.ts @@ -21,12 +21,12 @@ export default function createAggregateTests({ getService }: FtrProviderContext) it('should aggregate when there are no alerts', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/_aggregate` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_aggregate` ); expect(response.status).to.eql(200); expect(response.body).to.eql({ - alertExecutionStatus: { + rule_execution_status: { ok: 0, active: 0, error: 0, @@ -45,12 +45,12 @@ export default function createAggregateTests({ getService }: FtrProviderContext) [...Array(NumOkAlerts)].map(async () => { const okAlertId = await createTestAlert( { - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', schedule: { interval: '1s' }, }, 'ok' ); - objectRemover.add(Spaces.space1.id, okAlertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, okAlertId, 'rule', 'alerting'); }) ); @@ -58,7 +58,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) [...Array(NumActiveAlerts)].map(async () => { const activeAlertId = await createTestAlert( { - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, params: { pattern: { instance: new Array(100).fill(true) }, @@ -66,7 +66,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) }, 'active' ); - objectRemover.add(Spaces.space1.id, activeAlertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, activeAlertId, 'rule', 'alerting'); }) ); @@ -74,12 +74,12 @@ export default function createAggregateTests({ getService }: FtrProviderContext) [...Array(NumErrorAlerts)].map(async () => { const activeAlertId = await createTestAlert( { - alertTypeId: 'test.throw', + rule_type_id: 'test.throw', schedule: { interval: '1s' }, }, 'error' ); - objectRemover.add(Spaces.space1.id, activeAlertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, activeAlertId, 'rule', 'alerting'); }) ); @@ -88,12 +88,12 @@ export default function createAggregateTests({ getService }: FtrProviderContext) // too early. await delay(1000); const reponse = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/_aggregate` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rules/_aggregate` ); expect(reponse.status).to.eql(200); expect(reponse.body).to.eql({ - alertExecutionStatus: { + rule_execution_status: { ok: NumOkAlerts, active: NumActiveAlerts, error: NumErrorAlerts, @@ -102,6 +102,75 @@ export default function createAggregateTests({ getService }: FtrProviderContext) }, }); }); + + describe('legacy', () => { + it('should aggregate alert status totals', async () => { + const NumOkAlerts = 4; + const NumActiveAlerts = 1; + const NumErrorAlerts = 2; + + await Promise.all( + [...Array(NumOkAlerts)].map(async () => { + const okAlertId = await createTestAlert( + { + rule_type_id: 'test.noop', + schedule: { interval: '1s' }, + }, + 'ok' + ); + objectRemover.add(Spaces.space1.id, okAlertId, 'rule', 'alerting'); + }) + ); + + await Promise.all( + [...Array(NumActiveAlerts)].map(async () => { + const activeAlertId = await createTestAlert( + { + rule_type_id: 'test.patternFiring', + schedule: { interval: '1s' }, + params: { + pattern: { instance: new Array(100).fill(true) }, + }, + }, + 'active' + ); + objectRemover.add(Spaces.space1.id, activeAlertId, 'rule', 'alerting'); + }) + ); + + await Promise.all( + [...Array(NumErrorAlerts)].map(async () => { + const activeAlertId = await createTestAlert( + { + rule_type_id: 'test.throw', + schedule: { interval: '1s' }, + }, + 'error' + ); + objectRemover.add(Spaces.space1.id, activeAlertId, 'rule', 'alerting'); + }) + ); + + // Adding delay to allow ES refresh cycle to run. Even when the waitForStatus + // calls are successful, the call to aggregate may return stale totals if called + // too early. + await delay(1000); + const reponse = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/_aggregate` + ); + + expect(reponse.status).to.eql(200); + expect(reponse.body).to.eql({ + alertExecutionStatus: { + ok: NumOkAlerts, + active: NumActiveAlerts, + error: NumErrorAlerts, + pending: 0, + unknown: 0, + }, + }); + }); + }); }); const WaitForStatusIncrement = 500; @@ -116,11 +185,11 @@ export default function createAggregateTests({ getService }: FtrProviderContext) } const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${id}` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${id}` ); expect(response.status).to.eql(200); - const { executionStatus } = response.body || {}; + const { execution_status: executionStatus } = response.body || {}; const { status } = executionStatus || {}; const message = `waitForStatus(${Array.from(statuses)}): got ${JSON.stringify( @@ -144,7 +213,7 @@ export default function createAggregateTests({ getService }: FtrProviderContext) async function createTestAlert(testAlertOverrides = {}, status: string) { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData(testAlertOverrides)) .expect(200); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts index 49b4d7770962a..89f6f7f54f4c3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts @@ -46,11 +46,11 @@ export function alertTests({ getService }: FtrProviderContext, space: Space) { await esTestIndexTool.setup(); await es.indices.create({ index: authorizationIndex }); const { body: createdAction } = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My action', - actionTypeId: 'test.index-record', + connector_type_id: 'test.index-record', config: { unencrypted: `This value shouldn't get encrypted`, }, @@ -71,7 +71,7 @@ export function alertTests({ getService }: FtrProviderContext, space: Space) { after(async () => { await esTestIndexTool.destroy(); await es.indices.delete({ index: authorizationIndex }); - objectRemover.add(space.id, indexRecordActionId, 'action', 'actions'); + objectRemover.add(space.id, indexRecordActionId, 'connector', 'actions'); await objectRemover.removeAll(); }); @@ -142,11 +142,11 @@ instanceStateValue: true const reference = alertUtils.generateReference(); const { body: createdAction } = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) @@ -158,11 +158,11 @@ instanceStateValue: true }; const createdAlert = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, params: { @@ -189,7 +189,7 @@ instanceStateValue: true expect(createdAlert.status).to.eql(200); const alertId = createdAlert.body.id; - objectRemover.add(space.id, alertId, 'alert', 'alerts'); + objectRemover.add(space.id, alertId, 'rule', 'alerting'); const actionTestRecord = ( await esTestIndexTool.waitForDocs('action:test.index-record', reference) @@ -203,11 +203,11 @@ instanceStateValue: true const reference = alertUtils.generateReference(); const { body: createdAction } = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) @@ -219,11 +219,11 @@ instanceStateValue: true }; // created disabled alert const createdAlert = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, enabled: false, throttle: null, @@ -262,7 +262,7 @@ instanceStateValue: true const actionTestRecord = await esTestIndexTool.search('action:test.index-record', reference); expect(actionTestRecord.hits.total.value).to.eql(0); - objectRemover.add(space.id, alertId, 'alert', 'alerts'); + objectRemover.add(space.id, alertId, 'rule', 'alerting'); }); it('should reschedule failing alerts using the Task Manager retry logic with alert schedule interval', async () => { @@ -282,7 +282,7 @@ instanceStateValue: true await esTestIndexTool.waitForDocs('alert:test.failing', reference); await retry.try(async () => { - const alertTask = (await getAlertingTaskById(response.body.scheduledTaskId)).docs[0]; + const alertTask = (await getAlertingTaskById(response.body.scheduled_task_id)).docs[0]; expect(alertTask.status).to.eql('idle'); expect(alertTask.schedule.interval).to.eql('10s'); // ensure the alert is rescheduled correctly @@ -301,24 +301,24 @@ instanceStateValue: true const retryDate = new Date(Date.now() + 60000); const { body: createdAction } = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'Test rate limit', - actionTypeId: 'test.rate-limit', + connector_type_id: 'test.rate-limit', config: {}, }) .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ schedule: { interval: '1m' }, - alertTypeId: 'test.always-firing', + rule_type_id: 'test.always-firing', params: { index: ES_TEST_INDEX_NAME, reference: 'create-test-2', @@ -338,7 +338,7 @@ instanceStateValue: true ); expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); const scheduledActionTask = await retry.try(async () => { const searchResult = await es.search({ index: '.kibana_task_manager', @@ -382,11 +382,11 @@ instanceStateValue: true it('should have proper callCluster and savedObjectsClient authorization for alert type executor', async () => { const reference = alertUtils.generateReference(); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.authorization', + rule_type_id: 'test.authorization', params: { callClusterAuthorizationIndex: authorizationIndex, savedObjectsClientType: 'dashboard', @@ -398,7 +398,7 @@ instanceStateValue: true ); expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); const alertTestRecord = ( await esTestIndexTool.waitForDocs('alert:test.authorization', reference) )[0]; @@ -419,20 +419,20 @@ instanceStateValue: true it('should have proper callCluster and savedObjectsClient authorization for action type executor', async () => { const reference = alertUtils.generateReference(); const { body: createdAction } = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/actions/action`) + .post(`${getUrlPrefix(space.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My action', - actionTypeId: 'test.authorization', + connector_type_id: 'test.authorization', }) .expect(200); - objectRemover.add(space.id, createdAction.id, 'action', 'actions'); + objectRemover.add(space.id, createdAction.id, 'connector', 'actions'); const response = await supertestWithoutAuth - .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(space.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.always-firing', + rule_type_id: 'test.always-firing', params: { index: ES_TEST_INDEX_NAME, reference, @@ -454,7 +454,7 @@ instanceStateValue: true ); expect(response.statusCode).to.eql(200); - objectRemover.add(space.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(space.id, response.body.id, 'rule', 'alerting'); const actionTestRecord = ( await esTestIndexTool.waitForDocs('action:test.authorization', reference) )[0]; diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/alert.ts index 777caacd465d8..8511bcdf89d3b 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/es_query/alert.ts @@ -207,15 +207,16 @@ export default function alertTests({ getService }: FtrProviderContext) { }; const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send({ name: params.name, consumer: 'alerts', enabled: true, - alertTypeId: ALERT_TYPE_ID, + rule_type_id: ALERT_TYPE_ID, schedule: { interval: `${ALERT_INTERVAL_SECONDS}s` }, actions: [action], + notify_when: 'onActiveAlert', params: { index: [ES_TEST_INDEX_NAME], timeField: params.timeField || 'date', @@ -230,7 +231,7 @@ export default function alertTests({ getService }: FtrProviderContext) { .expect(200); const alertId = createdAlert.id; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); return alertId; } @@ -239,11 +240,11 @@ export default function alertTests({ getService }: FtrProviderContext) { async function createAction(supertest: any, objectRemover: ObjectRemover): Promise { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'index action for es query FT', - actionTypeId: ACTION_TYPE_ID, + connector_type_id: ACTION_TYPE_ID, config: { index: ES_TEST_OUTPUT_INDEX_NAME, }, @@ -252,7 +253,7 @@ async function createAction(supertest: any, objectRemover: ObjectRemover): Promi .expect(200); const actionId = createdAction.id; - objectRemover.add(Spaces.space1.id, actionId, 'action', 'actions'); + objectRemover.add(Spaces.space1.id, actionId, 'connector', 'actions'); return actionId; } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts index e0275f3ead37a..3d7e391d7530f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/builtin_alert_types/index_threshold/alert.ts @@ -357,15 +357,16 @@ export default function alertTests({ getService }: FtrProviderContext) { }; const { status, body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send({ name: params.name, consumer: 'alerts', enabled: true, - alertTypeId: ALERT_TYPE_ID, + rule_type_id: ALERT_TYPE_ID, schedule: { interval: `${ALERT_INTERVAL_SECONDS}s` }, actions: [action], + notify_when: 'onActiveAlert', params: { index: ES_TEST_INDEX_NAME, timeField: params.timeField || 'date', @@ -387,7 +388,7 @@ export default function alertTests({ getService }: FtrProviderContext) { expect(status).to.be(200); const alertId = createdAlert.id; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); return alertId; } @@ -396,11 +397,11 @@ export default function alertTests({ getService }: FtrProviderContext) { async function createAction(supertest: any, objectRemover: ObjectRemover): Promise { const { statusCode, body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'index action for index threshold FT', - actionTypeId: ACTION_TYPE_ID, + connector_type_id: ACTION_TYPE_ID, config: { index: ES_TEST_OUTPUT_INDEX_NAME, }, @@ -413,7 +414,7 @@ async function createAction(supertest: any, objectRemover: ObjectRemover): Promi expect(statusCode).to.be(200); const actionId = createdAction.id; - objectRemover.add(Spaces.space1.id, actionId, 'action', 'actions'); + objectRemover.add(Spaces.space1.id, actionId, 'connector', 'actions'); return actionId; } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index 6e0bf867c9587..9033b1f303943 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -35,18 +35,18 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ @@ -61,7 +61,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { ); expect(response.status).to.eql(200); - objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); expect(response.body).to.eql({ id: response.body.id, name: 'abc', @@ -69,34 +69,34 @@ export default function createAlertTests({ getService }: FtrProviderContext) { actions: [ { id: createdAction.id, - actionTypeId: createdAction.actionTypeId, + connector_type_id: createdAction.connector_type_id, group: 'default', params: {}, }, ], enabled: true, - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', params: {}, - createdBy: null, + created_by: null, schedule: { interval: '1m' }, - scheduledTaskId: response.body.scheduledTaskId, - updatedBy: null, - apiKeyOwner: null, + scheduled_task_id: response.body.scheduled_task_id, + updated_by: null, + api_key_owner: null, throttle: '1m', - notifyWhen: 'onThrottleInterval', - muteAll: false, - mutedInstanceIds: [], - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + notify_when: 'onThrottleInterval', + mute_all: false, + muted_alert_ids: [], + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.eql(Date.parse(response.body.createdAt)); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.eql(Date.parse(response.body.created_at)); - expect(typeof response.body.scheduledTaskId).to.be('string'); - const { _source: taskRecord } = await getScheduledTask(response.body.scheduledTaskId); + expect(typeof response.body.scheduled_task_id).to.be('string'); + const { _source: taskRecord } = await getScheduledTask(response.body.scheduled_task_id); expect(taskRecord.type).to.eql('task'); expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); expect(JSON.parse(taskRecord.task.params)).to.eql({ @@ -115,12 +115,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should allow providing custom saved object ids (uuid v1)', async () => { const customId = '09570bb0-6299-11eb-8fde-9fe5ce6ea450'; const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${customId}`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()); expect(response.status).to.eql(200); - objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); expect(response.body.id).to.eql(customId); // Ensure AAD isn't broken await checkAAD({ @@ -134,12 +134,12 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should allow providing custom saved object ids (uuid v4)', async () => { const customId = 'b3bc6d83-3192-4ffd-9702-ad4fb88617ba'; const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${customId}`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()); expect(response.status).to.eql(200); - objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); expect(response.body.id).to.eql(customId); // Ensure AAD isn't broken await checkAAD({ @@ -153,7 +153,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should not allow providing simple custom ids (non uuid)', async () => { const customId = '1'; const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${customId}`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()); @@ -169,13 +169,13 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should return 409 when document with id already exists', async () => { const customId = '5031f8f0-629a-11eb-b500-d1931a8e5df7'; const createdAlertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${customId}`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlertResponse.body.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlertResponse.body.id, 'rule', 'alerting'); await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${customId}`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${customId}`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(409); @@ -183,7 +183,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when consumer is unknown', async () => { const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ consumer: 'some consumer patrick invented' })); @@ -201,13 +201,101 @@ export default function createAlertTests({ getService }: FtrProviderContext) { it('should handle create alert request appropriately when an alert is disabled ', async () => { const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })); expect(response.status).to.eql(200); - objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); expect(response.body.scheduledTaskId).to.eql(undefined); }); + + describe('legacy', () => { + it('should handle create alert request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'MY action', + connector_type_id: 'test.noop', + config: {}, + secrets: {}, + }) + .expect(200); + + const { + rule_type_id: alertTypeId, + notify_when: notifyWhen, + ...testAlert + } = getTestAlertData({ + actions: [ + { + id: createdAction.id, + group: 'default', + params: {}, + }, + ], + }); + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send({ + ...testAlert, + alertTypeId, + notifyWhen, + }); + + expect(response.status).to.eql(200); + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); + expect(response.body).to.eql({ + id: response.body.id, + name: 'abc', + tags: ['foo'], + actions: [ + { + id: createdAction.id, + actionTypeId: createdAction.connector_type_id, + group: 'default', + params: {}, + }, + ], + enabled: true, + alertTypeId: 'test.noop', + consumer: 'alertsFixture', + params: {}, + createdBy: null, + schedule: { interval: '1m' }, + scheduledTaskId: response.body.scheduledTaskId, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + notifyWhen: 'onThrottleInterval', + muteAll: false, + mutedInstanceIds: [], + createdAt: response.body.createdAt, + updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, + }); + expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); + expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); + expect(Date.parse(response.body.updatedAt)).to.eql(Date.parse(response.body.createdAt)); + + expect(typeof response.body.scheduledTaskId).to.be('string'); + const { _source: taskRecord } = await getScheduledTask(response.body.scheduledTaskId); + expect(taskRecord.type).to.eql('task'); + expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); + expect(JSON.parse(taskRecord.task.params)).to.eql({ + alertId: response.body.id, + spaceId: Spaces.space1.id, + }); + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: Spaces.space1.id, + type: 'alert', + id: response.body.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts index 4e033eabc2ac2..04ae217d47760 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/delete.ts @@ -29,13 +29,13 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it('should handle delete alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); await supertest - .delete(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(204, ''); @@ -49,13 +49,13 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { it(`shouldn't delete alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); await supertest - .delete(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert/${createdAlert.id}`) + .delete(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(404, { statusCode: 404, @@ -63,5 +63,27 @@ export default function createDeleteTests({ getService }: FtrProviderContext) { message: `Saved object [alert/${createdAlert.id}] not found`, }); }); + + describe('legacy', () => { + it('should handle delete alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + + await supertest + .delete(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); + + try { + await getScheduledTask(createdAlert.scheduledTaskId); + throw new Error('Should have removed scheduled task'); + } catch (e) { + expect(e.status).to.eql(404); + } + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts index b2bb24aec902d..60749343cf269 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/disable.ts @@ -36,11 +36,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it('should handle disable alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.disable(createdAlert.id); @@ -62,11 +62,11 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte it(`shouldn't disable alert from another space`, async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: true })) .expect(200); - objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.other.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.getDisableRequest(createdAlert.id).expect(404, { statusCode: 404, @@ -74,5 +74,36 @@ export default function createDisableAlertTests({ getService }: FtrProviderConte message: `Saved object [alert/${createdAlert.id}] not found`, }); }); + + describe('legacy', () => { + it('should handle disable alert request appropriately', async () => { + const { body: createdAlert } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: true })) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_disable`) + .set('kbn-xsrf', 'foo') + .expect(204); + + try { + await getScheduledTask(createdAlert.scheduledTaskId); + throw new Error('Should have removed scheduled task'); + } catch (e) { + expect(e.status).to.eql(404); + } + + // Ensure AAD isn't broken + await checkAAD({ + supertest: supertestWithoutAuth, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts index 29347a2d1d398..f0b0d1f34a277 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/enable.ts @@ -36,20 +36,20 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it('should handle enable alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.enable(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); - expect(typeof updatedAlert.scheduledTaskId).to.eql('string'); - const { _source: taskRecord } = await getScheduledTask(updatedAlert.scheduledTaskId); + expect(typeof updatedAlert.scheduled_task_id).to.eql('string'); + const { _source: taskRecord } = await getScheduledTask(updatedAlert.scheduled_task_id); expect(taskRecord.type).to.eql('task'); expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); expect(JSON.parse(taskRecord.task.params)).to.eql({ @@ -68,11 +68,11 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex it(`shouldn't enable alert from another space`, async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.other.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.getEnableRequest(createdAlert.id).expect(404, { statusCode: 404, @@ -80,5 +80,42 @@ export default function createEnableAlertTests({ getService }: FtrProviderContex message: `Saved object [alert/${createdAlert.id}] not found`, }); }); + + describe('legacy', () => { + it('should handle enable alert request appropriately', async () => { + const { body: createdAlert } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: false })) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_enable`) + .set('kbn-xsrf', 'foo') + .expect(204); + + const { body: updatedAlert } = await supertestWithoutAuth + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .expect(200); + expect(typeof updatedAlert.scheduled_task_id).to.eql('string'); + const { _source: taskRecord } = await getScheduledTask(updatedAlert.scheduled_task_id); + expect(taskRecord.type).to.eql('task'); + expect(taskRecord.task.taskType).to.eql('alerting:test.noop'); + expect(JSON.parse(taskRecord.task.params)).to.eql({ + alertId: createdAlert.id, + spaceId: Spaces.space1.id, + }); + + // Ensure AAD isn't broken + await checkAAD({ + supertest: supertestWithoutAuth, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts index 2e97711cdef2c..5d54fe3d2b1f7 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/event_log.ts @@ -26,11 +26,11 @@ export default function eventLogTests({ getService }: FtrProviderContext) { it('should generate expected events for normal operation', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) @@ -42,11 +42,11 @@ export default function eventLogTests({ getService }: FtrProviderContext) { }; const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, params: { @@ -64,7 +64,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { expect(response.status).to.eql(200); const alertId = response.body.id; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); // get the events we're expecting const events = await retry.try(async () => { @@ -174,11 +174,11 @@ export default function eventLogTests({ getService }: FtrProviderContext) { it('should generate expected events for normal operation with subgroups', async () => { const { body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'MY action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) @@ -191,11 +191,11 @@ export default function eventLogTests({ getService }: FtrProviderContext) { }; const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, throttle: null, params: { @@ -213,7 +213,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { expect(response.status).to.eql(200); const alertId = response.body.id; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); // get the events we're expecting const events = await retry.try(async () => { @@ -315,11 +315,11 @@ export default function eventLogTests({ getService }: FtrProviderContext) { it('should generate events for execution errors', async () => { const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.throw', + rule_type_id: 'test.throw', schedule: { interval: '1s' }, throttle: null, }) @@ -327,7 +327,7 @@ export default function eventLogTests({ getService }: FtrProviderContext) { expect(response.status).to.eql(200); const alertId = response.body.id; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); const events = await retry.try(async () => { return await getEventLog({ diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts index f2a79b72dc731..0f7ed80cfd38d 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts @@ -28,15 +28,19 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should be "pending" for newly created alert', async () => { const dateStart = Date.now(); const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()); const dateEnd = Date.now(); expect(response.status).to.eql(200); - objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); - - expect(response.body.executionStatus).to.be.ok(); - const { status, lastExecutionDate, error } = response.body.executionStatus; + objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting'); + + expect(response.body.execution_status).to.be.ok(); + const { + error, + status, + last_execution_date: lastExecutionDate, + } = response.body.execution_status; expect(status).to.be('pending'); ensureDatetimesAreOrdered([dateStart, lastExecutionDate, dateEnd]); expect(error).not.to.be.ok(); @@ -54,25 +58,25 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon const dates = []; dates.push(Date.now()); const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', schedule: { interval: '1s' }, }) ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; - dates.push(response.body.executionStatus.lastExecutionDate); - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + const alertUpdatedAt = response.body.updated_at; + dates.push(response.body.execution_status.last_execution_date); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); const executionStatus = await waitForStatus(alertId, new Set(['ok'])); - dates.push(executionStatus.lastExecutionDate); + dates.push(executionStatus.last_execution_date); dates.push(Date.now()); ensureDatetimesAreOrdered(dates); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); + await ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); // Ensure AAD isn't broken await checkAAD({ @@ -87,11 +91,11 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon const dates = []; dates.push(Date.now()); const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', schedule: { interval: '1s' }, params: { pattern: { instance: trues(100) }, @@ -100,15 +104,15 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; - dates.push(response.body.executionStatus.lastExecutionDate); - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + const alertUpdatedAt = response.body.updated_at; + dates.push(response.body.execution_status.last_execution_date); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); const executionStatus = await waitForStatus(alertId, new Set(['active'])); - dates.push(executionStatus.lastExecutionDate); + dates.push(executionStatus.last_execution_date); dates.push(Date.now()); ensureDatetimesAreOrdered(dates); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); + await ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); // Ensure AAD isn't broken await checkAAD({ @@ -123,25 +127,25 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon const dates = []; dates.push(Date.now()); const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.throw', + rule_type_id: 'test.throw', schedule: { interval: '1s' }, }) ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; - dates.push(response.body.executionStatus.lastExecutionDate); - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + const alertUpdatedAt = response.body.updated_at; + dates.push(response.body.execution_status.last_execution_date); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); const executionStatus = await waitForStatus(alertId, new Set(['error'])); - dates.push(executionStatus.lastExecutionDate); + dates.push(executionStatus.last_execution_date); dates.push(Date.now()); ensureDatetimesAreOrdered(dates); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); + await ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); // Ensure AAD isn't broken await checkAAD({ @@ -159,41 +163,41 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should eventually have error reason "execute" when appropriate', async () => { const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.throw', + rule_type_id: 'test.throw', schedule: { interval: '1s' }, }) ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + const alertUpdatedAt = response.body.updated_at; + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); const executionStatus = await waitForStatus(alertId, new Set(['error'])); expect(executionStatus.error).to.be.ok(); expect(executionStatus.error.reason).to.be('execute'); expect(executionStatus.error.message).to.be('this alert is intended to fail'); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); + await ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); }); it('should eventually have error reason "unknown" when appropriate', async () => { const response = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.validation', + rule_type_id: 'test.validation', schedule: { interval: '1s' }, params: { param1: 'valid now, but will change to a number soon!' }, }) ); expect(response.status).to.eql(200); const alertId = response.body.id; - const alertUpdatedAt = response.body.updatedAt; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + const alertUpdatedAt = response.body.updated_at; + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); let executionStatus = await waitForStatus(alertId, new Set(['ok'])); @@ -211,7 +215,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon executionStatus = await waitForStatus(alertId, new Set(['error'])); expect(executionStatus.error).to.be.ok(); expect(executionStatus.error.reason).to.be('unknown'); - ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); + await ensureAlertUpdatedAtHasNotChanged(alertId, alertUpdatedAt); const message = 'params invalid: [param1]: expected value of type [string] but got [number]'; expect(executionStatus.error.message).to.be(message); @@ -220,17 +224,17 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should be able to find over all the fields', async () => { const startDate = Date.now(); const createResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.throw', + rule_type_id: 'test.throw', schedule: { interval: '1s' }, }) ); expect(createResponse.status).to.eql(200); const alertId = createResponse.body.id; - objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, alertId, 'rule', 'alerting'); await waitForStatus(alertId, new Set(['error'])); @@ -264,11 +268,11 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon } const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${id}` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${id}` ); expect(response.status).to.eql(200); - const { executionStatus } = response.body || {}; + const { execution_status: executionStatus } = response.body || {}; const { status } = executionStatus || {}; const message = `waitForStatus(${Array.from(statuses)}): got ${JSON.stringify( @@ -300,7 +304,8 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon const response = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/${findUri}`); expect(response.status).to.eql(200); - const { executionStatus } = response.body.data.find((obj: any) => obj.id === id) || {}; + const { execution_status: executionStatus } = + response.body.data.find((obj: any) => obj.id === id) || {}; const { status } = executionStatus || {}; const message = `waitForFindStatus(${Array.from(statuses)}): got ${JSON.stringify( @@ -320,12 +325,12 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon async function ensureAlertUpdatedAtHasNotChanged(alertId: string, originalUpdatedAt: string) { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${alertId}` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${alertId}` ); - const { updatedAt, executionStatus } = response.body; + const { updated_at: updatedAt, execution_status: executionStatus } = response.body; expect(Date.parse(updatedAt)).to.be.greaterThan(0); expect(Date.parse(updatedAt)).to.eql(Date.parse(originalUpdatedAt)); - expect(Date.parse(executionStatus.lastExecutionDate)).to.be.greaterThan( + expect(Date.parse(executionStatus.last_execution_date)).to.be.greaterThan( Date.parse(originalUpdatedAt) ); } @@ -335,7 +340,7 @@ function expectErrorExecutionStatus(executionStatus: Record, startD expect(executionStatus).to.be.ok(); expect(executionStatus.status).to.equal('error'); - const statusDate = Date.parse(executionStatus.lastExecutionDate); + const statusDate = Date.parse(executionStatus.last_execution_date); const stopDate = Date.now(); expect(startDate).to.be.lessThan(statusDate); expect(stopDate).to.be.greaterThan(statusDate); @@ -345,7 +350,7 @@ function expectErrorExecutionStatus(executionStatus: Record, startD } function getFindUri(filter: string) { - return `api/alerts/_find?filter=alert.attributes.executionStatus.${filter}`; + return `api/alerting/rules/_find?filter=alert.attributes.executionStatus.${filter}`; } function trues(length: number): boolean[] { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index 1493c99162bf5..191bcb1bb52f0 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -21,76 +21,76 @@ export default function createFindTests({ getService }: FtrProviderContext) { async function createAlert(overwrites = {}) { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData(overwrites)) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); return createdAlert; } it('should handle find alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const response = await supertest.get( `${getUrlPrefix( Spaces.space1.id - )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ); expect(response.status).to.eql(200); expect(response.body.page).to.equal(1); - expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.per_page).to.be.greaterThan(0); expect(response.body.total).to.be.greaterThan(0); const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); expect(match).to.eql({ id: createdAlert.id, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', schedule: { interval: '1m' }, enabled: true, actions: [], params: {}, - createdBy: null, - apiKeyOwner: null, - scheduledTaskId: match.scheduledTaskId, - updatedBy: null, + created_by: null, + api_key_owner: null, + scheduled_task_id: match.scheduled_task_id, + updated_by: null, throttle: '1m', - notifyWhen: 'onThrottleInterval', - muteAll: false, - mutedInstanceIds: [], - createdAt: match.createdAt, - updatedAt: match.updatedAt, - executionStatus: match.executionStatus, + notify_when: 'onThrottleInterval', + mute_all: false, + muted_alert_ids: [], + created_at: match.created_at, + updated_at: match.updated_at, + execution_status: match.execution_status, }); - expect(Date.parse(match.createdAt)).to.be.greaterThan(0); - expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); + expect(Date.parse(match.created_at)).to.be.greaterThan(0); + expect(Date.parse(match.updated_at)).to.be.greaterThan(0); }); it(`shouldn't find alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await supertest .get( `${getUrlPrefix( Spaces.other.id - )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` + )}/api/alerting/rules/_find?search=test.noop&search_fields=alertTypeId` ) .expect(200, { page: 1, - perPage: 10, + per_page: 10, total: 0, data: [], }); @@ -106,12 +106,59 @@ export default function createFindTests({ getService }: FtrProviderContext) { const response = await supertest.get( `${getUrlPrefix( Spaces.space1.id - )}/api/alerts/_find?filter=alert.attributes.params.strValue:"my b"` + )}/api/alerting/rules/_find?filter=alert.attributes.params.strValue:"my b"` ); expect(response.status).to.eql(200); expect(response.body.total).to.equal(1); expect(response.body.data[0].params.strValue).to.eql('my b'); }); + + describe('legacy', () => { + it('should handle find alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertest.get( + `${getUrlPrefix( + Spaces.space1.id + )}/api/alerts/_find?search=test.noop&search_fields=alertTypeId` + ); + + expect(response.status).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const match = response.body.data.find((obj: any) => obj.id === createdAlert.id); + expect(match).to.eql({ + id: createdAlert.id, + name: 'abc', + tags: ['foo'], + alertTypeId: 'test.noop', + consumer: 'alertsFixture', + schedule: { interval: '1m' }, + enabled: true, + actions: [], + params: {}, + createdBy: null, + apiKeyOwner: null, + scheduledTaskId: match.scheduledTaskId, + updatedBy: null, + throttle: '1m', + notifyWhen: 'onThrottleInterval', + muteAll: false, + mutedInstanceIds: [], + createdAt: match.createdAt, + updatedAt: match.updatedAt, + executionStatus: match.executionStatus, + }); + expect(Date.parse(match.createdAt)).to.be.greaterThan(0); + expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts index 3846e35f34fb1..5245488dcb7b4 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts @@ -21,14 +21,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('should handle get alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}` ); expect(response.status).to.eql(200); @@ -36,38 +36,38 @@ export default function createGetTests({ getService }: FtrProviderContext) { id: createdAlert.id, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', schedule: { interval: '1m' }, enabled: true, actions: [], params: {}, - createdBy: null, - scheduledTaskId: response.body.scheduledTaskId, - updatedBy: null, - apiKeyOwner: null, + created_by: null, + scheduled_task_id: response.body.scheduled_task_id, + updated_by: null, + api_key_owner: null, throttle: '1m', - notifyWhen: 'onThrottleInterval', - muteAll: false, - mutedInstanceIds: [], - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + notify_when: 'onThrottleInterval', + mute_all: false, + muted_alert_ids: [], + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); }); it(`shouldn't find alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await supertest - .get(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule/${createdAlert.id}`) .expect(404, { statusCode: 404, error: 'Not Found', @@ -76,11 +76,52 @@ export default function createGetTests({ getService }: FtrProviderContext) { }); it(`should handle get alert request appropriately when alert doesn't exist`, async () => { - await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/1`).expect(404, { + await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/1`).expect(404, { statusCode: 404, error: 'Not Found', message: 'Saved object [alert/1] not found', }); }); + + describe('legacy', () => { + it('should handle get alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}` + ); + + expect(response.status).to.eql(200); + expect(response.body).to.eql({ + id: createdAlert.id, + name: 'abc', + tags: ['foo'], + alertTypeId: 'test.noop', + consumer: 'alertsFixture', + schedule: { interval: '1m' }, + enabled: true, + actions: [], + params: {}, + createdBy: null, + scheduledTaskId: response.body.scheduledTaskId, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + notifyWhen: 'onThrottleInterval', + muteAll: false, + mutedInstanceIds: [], + createdAt: response.body.createdAt, + updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, + }); + expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); + expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_instance_summary.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_instance_summary.ts index 25d9efdebdfb0..099502e375faa 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_instance_summary.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_instance_summary.ts @@ -32,7 +32,7 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr it(`handles non-existant alert`, async () => { await supertest - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/1/_instance_summary`) + .get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/1/_alert_summary`) .expect(404, { statusCode: 404, error: 'Not Found', @@ -42,106 +42,106 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr it('handles no-op alert', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await waitForEvents(createdAlert.id, ['execute']); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/_alert_summary` ); expect(response.status).to.eql(200); - const { statusStartDate, statusEndDate } = response.body; + const { status_start_date: statusStartDate, status_end_date: statusEndDate } = response.body; expect(Date.parse(statusStartDate)).to.be.lessThan(Date.parse(statusEndDate)); - const stableBody = omit(response.body, ['statusStartDate', 'statusEndDate', 'lastRun']); + const stableBody = omit(response.body, ['status_start_date', 'status_end_date', 'last_run']); expect(stableBody).to.eql({ id: createdAlert.id, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', - muteAll: false, + mute_all: false, throttle: '1m', enabled: true, - errorMessages: [], - instances: {}, + error_messages: [], + alerts: {}, }); }); it('handles no-op alert without waiting for execution event', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/_alert_summary` ); expect(response.status).to.eql(200); - const { statusStartDate, statusEndDate } = response.body; + const { status_start_date: statusStartDate, status_end_date: statusEndDate } = response.body; expect(Date.parse(statusStartDate)).to.be.lessThan(Date.parse(statusEndDate)); - const stableBody = omit(response.body, ['statusStartDate', 'statusEndDate', 'lastRun']); + const stableBody = omit(response.body, ['status_start_date', 'status_end_date', 'last_run']); expect(stableBody).to.eql({ id: createdAlert.id, name: 'abc', tags: ['foo'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', status: 'OK', - muteAll: false, + mute_all: false, throttle: '1m', enabled: true, - errorMessages: [], - instances: {}, + error_messages: [], + alerts: {}, }); }); it('handles dateStart parameter', async () => { const dateStart = '2020-08-08T08:08:08.008Z'; const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await waitForEvents(createdAlert.id, ['execute']); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${ + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ createdAlert.id - }/_instance_summary?dateStart=${dateStart}` + }/_alert_summary?date_start=${dateStart}` ); expect(response.status).to.eql(200); - const { statusStartDate, statusEndDate } = response.body; + const { status_start_date: statusStartDate, status_end_date: statusEndDate } = response.body; expect(Date.parse(statusStartDate)).to.be.lessThan(Date.parse(statusEndDate)); expect(statusStartDate).to.be(dateStart); }); it('handles invalid dateStart parameter', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await waitForEvents(createdAlert.id, ['execute']); const dateStart = 'X0X0-08-08T08:08:08.008Z'; const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${ + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${ createdAlert.id - }/_instance_summary?dateStart=${dateStart}` + }/_alert_summary?date_start=${dateStart}` ); expect(response.status).to.eql(400); expect(response.body).to.eql({ @@ -153,20 +153,20 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr it('handles muted instances', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.muteInstance(createdAlert.id, '1'); await waitForEvents(createdAlert.id, ['execute']); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/_alert_summary` ); expect(response.status).to.eql(200); - expect(response.body.instances).to.eql({ + expect(response.body.alerts).to.eql({ '1': { status: 'OK', muted: true, @@ -177,17 +177,17 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr it('handles alert errors', async () => { const dateNow = Date.now(); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') - .send(getTestAlertData({ alertTypeId: 'test.throw' })) + .send(getTestAlertData({ rule_type_id: 'test.throw' })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await waitForEvents(createdAlert.id, ['execute']); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/_alert_summary` ); - const { errorMessages } = response.body; + const { error_messages: errorMessages } = response.body; expect(errorMessages.length).to.be.greaterThan(0); const errorMessage = errorMessages[0]; expect(Date.parse(errorMessage.date)).to.be.greaterThan(dateNow); @@ -203,26 +203,26 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr }; const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, }) ) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.muteInstance(createdAlert.id, 'instanceC'); await alertUtils.muteInstance(createdAlert.id, 'instanceD'); await waitForEvents(createdAlert.id, ['new-instance', 'recovered-instance']); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/_alert_summary` ); - const actualInstances = response.body.instances; + const actualInstances = response.body.alerts; const expectedInstances = { instanceA: { status: 'Active', @@ -247,6 +247,62 @@ export default function createGetAlertInstanceSummaryTests({ getService }: FtrPr }; expect(actualInstances).to.eql(expectedInstances); }); + + describe('legacy', () => { + it('handles multi-instance status', async () => { + // pattern of when the alert should fire + const pattern = { + instanceA: [true, true, true, true], + instanceB: [true, true, false, false], + instanceC: [true, true, true, true], + }; + + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + rule_type_id: 'test.patternFiring', + params: { pattern }, + schedule: { interval: '1s' }, + }) + ) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await alertUtils.muteInstance(createdAlert.id, 'instanceC'); + await alertUtils.muteInstance(createdAlert.id, 'instanceD'); + await waitForEvents(createdAlert.id, ['new-instance', 'recovered-instance']); + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_instance_summary` + ); + + const actualInstances = response.body.instances; + const expectedInstances = { + instanceA: { + status: 'Active', + muted: false, + actionGroupId: 'default', + activeStartDate: actualInstances.instanceA.activeStartDate, + }, + instanceB: { + status: 'OK', + muted: false, + }, + instanceC: { + status: 'Active', + muted: true, + actionGroupId: 'default', + activeStartDate: actualInstances.instanceC.activeStartDate, + }, + instanceD: { + status: 'OK', + muted: true, + }, + }; + expect(actualInstances).to.eql(expectedInstances); + }); + }); }); async function waitForEvents(id: string, actions: string[]) { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts index e3a23e499149b..9154c85af1bc7 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get_alert_state.ts @@ -22,57 +22,58 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it('should handle getAlertState request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/state` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/state` ); expect(response.status).to.eql(200); - expect(response.body).to.key('alertInstances', 'previousStartedAt'); + expect(response.body).to.key('alerts', 'previous_started_at'); }); it('should fetch updated state', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send({ enabled: true, name: 'abc', tags: ['foo'], - alertTypeId: 'test.cumulative-firing', + rule_type_id: 'test.cumulative-firing', consumer: 'alertsFixture', schedule: { interval: '5s' }, throttle: '5s', actions: [], params: {}, + notify_when: 'onThrottleInterval', }) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); // wait for alert to actually execute await retry.try(async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/state` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/state` ); expect(response.status).to.eql(200); - expect(response.body).to.key('alertInstances', 'alertTypeState', 'previousStartedAt'); - expect(response.body.alertTypeState.runCount).to.greaterThan(1); + expect(response.body).to.key('alerts', 'rule_type_state', 'previous_started_at'); + expect(response.body.rule_type_state.runCount).to.greaterThan(1); }); const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/state` + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/state` ); - expect(response.body.alertTypeState.runCount).to.greaterThan(0); + expect(response.body.rule_type_state.runCount).to.greaterThan(0); - const alertInstances = Object.entries>(response.body.alertInstances); - expect(alertInstances.length).to.eql(response.body.alertTypeState.runCount); + const alertInstances = Object.entries>(response.body.alerts); + expect(alertInstances.length).to.eql(response.body.rule_type_state.runCount); alertInstances.forEach(([key, value], index) => { expect(key).to.eql(`instance-${index}`); expect(value.state).to.eql({ instanceStateValue: true }); @@ -81,12 +82,58 @@ export default function createGetAlertStateTests({ getService }: FtrProviderCont it(`should handle getAlertState request appropriately when alert doesn't exist`, async () => { await supertest - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/1/state`) + .get(`${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/1/state`) .expect(404, { statusCode: 404, error: 'Not Found', message: 'Saved object [alert/1] not found', }); }); + + describe('legacy', () => { + it('should fetch updated state', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send({ + enabled: true, + name: 'abc', + tags: ['foo'], + rule_type_id: 'test.cumulative-firing', + consumer: 'alertsFixture', + schedule: { interval: '5s' }, + throttle: '5s', + actions: [], + params: {}, + notify_when: 'onThrottleInterval', + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + // wait for alert to actually execute + await retry.try(async () => { + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/state` + ); + + expect(response.status).to.eql(200); + expect(response.body).to.key('alertInstances', 'alertTypeState', 'previousStartedAt'); + expect(response.body.alertTypeState.runCount).to.greaterThan(1); + }); + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/internal/alerting/rule/${createdAlert.id}/state` + ); + + expect(response.body.rule_type_state.runCount).to.greaterThan(0); + + const alertInstances = Object.entries>(response.body.alerts); + expect(alertInstances.length).to.eql(response.body.rule_type_state.runCount); + alertInstances.forEach(([key, value], index) => { + expect(key).to.eql(`instance-${index}`); + expect(value.state).to.eql({ instanceStateValue: true }); + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts index c1050d26e083b..e9aeec1717c96 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts @@ -23,7 +23,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./get_alert_state')); loadTestFile(require.resolve('./get_alert_instance_summary')); - loadTestFile(require.resolve('./list_alert_types')); + loadTestFile(require.resolve('./rule_types')); loadTestFile(require.resolve('./event_log')); loadTestFile(require.resolve('./execution_status')); loadTestFile(require.resolve('./mute_all')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts index 3954dbdb337a3..30e69d62933e0 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/migrations.ts @@ -25,7 +25,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('7.10.0 migrates the `alerting` consumer to be the `alerts`', async () => { const response = await supertest.get( - `${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-92ee22728e6e` + `${getUrlPrefix(``)}/api/alerting/rule/74f3e6d7-b7bb-477d-ac28-92ee22728e6e` ); expect(response.status).to.eql(200); @@ -34,7 +34,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('7.10.0 migrates the `metrics` consumer to be the `infrastructure`', async () => { const response = await supertest.get( - `${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-fdf248d5f2a4` + `${getUrlPrefix(``)}/api/alerting/rule/74f3e6d7-b7bb-477d-ac28-fdf248d5f2a4` ); expect(response.status).to.eql(200); @@ -43,14 +43,14 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('7.10.0 migrates PagerDuty actions to have a default dedupKey', async () => { const response = await supertest.get( - `${getUrlPrefix(``)}/api/alerts/alert/b6087f72-994f-46fb-8120-c6e5c50d0f8f` + `${getUrlPrefix(``)}/api/alerting/rule/b6087f72-994f-46fb-8120-c6e5c50d0f8f` ); expect(response.status).to.eql(200); expect(response.body.actions).to.eql([ { - actionTypeId: '.pagerduty', + connector_type_id: '.pagerduty', id: 'a6a8ab7a-35cf-445e-ade3-215a029c2ee3', group: 'default', params: { @@ -60,7 +60,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }, }, { - actionTypeId: '.pagerduty', + connector_type_id: '.pagerduty', id: 'a6a8ab7a-35cf-445e-ade3-215a029c2ee3', group: 'default', params: { @@ -71,7 +71,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }, }, { - actionTypeId: '.pagerduty', + connector_type_id: '.pagerduty', id: 'a6a8ab7a-35cf-445e-ade3-215a029c2ee3', group: 'default', params: { @@ -86,32 +86,32 @@ export default function createGetTests({ getService }: FtrProviderContext) { it('7.11.0 migrates alerts to contain `updatedAt` field', async () => { const response = await supertest.get( - `${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-92ee22728e6e` + `${getUrlPrefix(``)}/api/alerting/rule/74f3e6d7-b7bb-477d-ac28-92ee22728e6e` ); expect(response.status).to.eql(200); - expect(response.body.updatedAt).to.eql('2020-06-17T15:35:39.839Z'); + expect(response.body.updated_at).to.eql('2020-06-17T15:35:39.839Z'); }); it('7.11.0 migrates alerts to contain `notifyWhen` field', async () => { const response = await supertest.get( - `${getUrlPrefix(``)}/api/alerts/alert/74f3e6d7-b7bb-477d-ac28-92ee22728e6e` + `${getUrlPrefix(``)}/api/alerting/rule/74f3e6d7-b7bb-477d-ac28-92ee22728e6e` ); expect(response.status).to.eql(200); - expect(response.body.notifyWhen).to.eql('onActiveAlert'); + expect(response.body.notify_when).to.eql('onActiveAlert'); }); it('7.11.2 migrates alerts with case actions, case fields are nested in an incident object', async () => { const response = await supertest.get( - `${getUrlPrefix(``)}/api/alerts/alert/99f3e6d7-b7bb-477d-ac28-92ee22726969` + `${getUrlPrefix(``)}/api/alerting/rule/99f3e6d7-b7bb-477d-ac28-92ee22726969` ); expect(response.status).to.eql(200); expect(response.body.actions).to.eql([ { id: '66a8ab7a-35cf-445e-ade3-215a029c6969', - actionTypeId: '.servicenow', + connector_type_id: '.servicenow', group: 'threshold met', params: { subAction: 'pushToService', @@ -129,7 +129,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }, { id: '66a8ab7a-35cf-445e-ade3-215a029c6969', - actionTypeId: '.jira', + connector_type_id: '.jira', group: 'threshold met', params: { subAction: 'pushToService', @@ -153,7 +153,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { }, { id: '66a8ab7a-35cf-445e-ade3-215a029c6969', - actionTypeId: '.resilient', + connector_type_id: '.resilient', group: 'threshold met', params: { subAction: 'pushToService', diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts index ae24994588348..8d300733bafc3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mustache_templates.ts @@ -63,11 +63,11 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should handle escapes in webhook', async () => { const url = formatUrl(new URL(webhookSimulatorURL), { auth: false }); const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'test') .send({ name: 'testing mustache escapes for webhook', - actionTypeId: '.webhook', + connector_type_id: '.webhook', secrets: {}, config: { headers: { @@ -78,19 +78,19 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon }); expect(actionResponse.status).to.eql(200); const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); // from x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts, // const EscapableStrings const varsTemplate = '{{context.escapableDoubleQuote}} -- {{context.escapableLineFeed}}'; const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ name: 'testing variable escapes for webhook', - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern: { instance: [true] }, }, @@ -107,7 +107,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(alertResponse.status).to.eql(200); const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const body = await retry.try(async () => waitForActionBody(webhookSimulatorURL, createdAlert.id) @@ -117,18 +117,18 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should handle escapes in slack', async () => { const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'test') .send({ name: "testing backtic'd mustache escapes for slack", - actionTypeId: '.slack', + connector_type_id: '.slack', secrets: { webhookUrl: slackSimulatorURL, }, }); expect(actionResponse.status).to.eql(200); const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); // from x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts, // const EscapableStrings @@ -136,12 +136,12 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon '{{context.escapableBacktic}} -- {{context.escapableBold}} -- {{context.escapableBackticBold}} -- {{context.escapableHtml}}'; const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ name: 'testing variable escapes for slack', - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern: { instance: [true] }, }, @@ -158,7 +158,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(alertResponse.status).to.eql(200); const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, createdAlert.id) @@ -168,30 +168,30 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should handle context variable object expansion', async () => { const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'test') .send({ name: 'testing context variable expansion', - actionTypeId: '.slack', + connector_type_id: '.slack', secrets: { webhookUrl: slackSimulatorURL, }, }); expect(actionResponse.status).to.eql(200); const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); // from x-pack/test/alerting_api_integration/common/fixtures/plugins/alerts/server/alert_types.ts, // const DeepContextVariables const varsTemplate = '{{context.deep}}'; const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ name: 'testing context variable expansion', - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern: { instance: [true, true] }, }, @@ -208,7 +208,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(alertResponse.status).to.eql(200); const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, createdAlert.id) @@ -220,28 +220,28 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should render kibanaBaseUrl as empty string since not configured', async () => { const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'test') .send({ name: 'testing context variable expansion', - actionTypeId: '.slack', + connector_type_id: '.slack', secrets: { webhookUrl: slackSimulatorURL, }, }); expect(actionResponse.status).to.eql(200); const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); const varsTemplate = 'kibanaBaseUrl: "{{kibanaBaseUrl}}"'; const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ name: 'testing context variable kibanaBaseUrl', - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern: { instance: [true, true] }, }, @@ -258,7 +258,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(alertResponse.status).to.eql(200); const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, createdAlert.id) @@ -269,11 +269,11 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon it('should render action variables in rule action', async () => { const url = formatUrl(new URL(webhookSimulatorURL), { auth: false }); const actionResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'test') .send({ name: 'testing action variable rendering', - actionTypeId: '.webhook', + connector_type_id: '.webhook', secrets: {}, config: { headers: { @@ -284,15 +284,15 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon }); expect(actionResponse.status).to.eql(200); const createdAction = actionResponse.body; - objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + objectRemover.add(Spaces.space1.id, createdAction.id, 'connector', 'actions'); const alertResponse = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ name: 'testing variable escapes for webhook', - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern: { instance: [true] }, }, @@ -309,7 +309,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon ); expect(alertResponse.status).to.eql(200); const createdAlert = alertResponse.body; - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const body = await retry.try(async () => waitForActionBody(webhookSimulatorURL, createdAlert.id) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts index feac0849a2864..c21a13edbf2cb 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_all.ts @@ -28,19 +28,19 @@ export default function createMuteTests({ getService }: FtrProviderContext) { it('should handle mute alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.muteAll(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); - expect(updatedAlert.muteAll).to.eql(true); + expect(updatedAlert.mute_all).to.eql(true); // Ensure AAD isn't broken await checkAAD({ @@ -50,5 +50,35 @@ export default function createMuteTests({ getService }: FtrProviderContext) { id: createdAlert.id, }); }); + + describe('legacy', () => { + it('should handle mute alert request appropriately', async () => { + const { body: createdAlert } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: false })) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_mute_all`) + .set('kbn-xsrf', 'foo') + .expect(204); + + const { body: updatedAlert } = await supertestWithoutAuth + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .expect(200); + expect(updatedAlert.mute_all).to.eql(true); + + // Ensure AAD isn't broken + await checkAAD({ + supertest: supertestWithoutAuth, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts index 0205485e322ea..afe29280748a5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/mute_instance.ts @@ -28,19 +28,19 @@ export default function createMuteInstanceTests({ getService }: FtrProviderConte it('should handle mute alert instance request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.muteInstance(createdAlert.id, '1'); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql(['1']); + expect(updatedAlert.muted_alert_ids).to.eql(['1']); // Ensure AAD isn't broken await checkAAD({ @@ -50,5 +50,39 @@ export default function createMuteInstanceTests({ getService }: FtrProviderConte id: createdAlert.id, }); }); + + describe('legacy', () => { + it('should handle mute alert instance request appropriately', async () => { + const { body: createdAlert } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: false })) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${ + createdAlert.id + }/alert_instance/1/_mute` + ) + .set('kbn-xsrf', 'foo') + .expect(204); + + const { body: updatedAlert } = await supertestWithoutAuth + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .expect(200); + expect(updatedAlert.muted_alert_ids).to.eql(['1']); + + // Ensure AAD isn't broken + await checkAAD({ + supertest: supertestWithoutAuth, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts index 29720f8d60ff5..7f1b82614a100 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/notify_when.ts @@ -24,22 +24,22 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext it(`alert with notifyWhen=onActiveAlert should always execute actions `, async () => { const { body: defaultAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My Default Action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: recoveredAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My Recovered Action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) @@ -52,15 +52,15 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext active ? 'default' : 'recovered' ); const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, throttle: null, - notifyWhen: 'onActiveAlert', + notify_when: 'onActiveAlert', actions: [ { id: defaultAction.id, @@ -76,7 +76,7 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext }) ) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const events = await retry.try(async () => { return await getEventLog({ @@ -101,22 +101,22 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext it(`alert with notifyWhen=onActionGroupChange should execute actions when action group changes`, async () => { const { body: defaultAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My Default Action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: recoveredAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My Recovered Action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) @@ -128,15 +128,15 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext const expectedActionGroupBasedOnPattern = ['default', 'recovered', 'default', 'recovered']; const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, throttle: null, - notifyWhen: 'onActionGroupChange', + notify_when: 'onActionGroupChange', actions: [ { id: defaultAction.id, @@ -152,7 +152,7 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext }) ) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const events = await retry.try(async () => { return await getEventLog({ @@ -177,22 +177,22 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext it(`alert with notifyWhen=onActionGroupChange should only execute actions when action subgroup changes`, async () => { const { body: defaultAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My Default Action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) .expect(200); const { body: recoveredAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`) .set('kbn-xsrf', 'foo') .send({ name: 'My Recovered Action', - actionTypeId: 'test.noop', + connector_type_id: 'test.noop', config: {}, secrets: {}, }) @@ -219,15 +219,15 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext ]; const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.patternFiring', + rule_type_id: 'test.patternFiring', params: { pattern }, schedule: { interval: '1s' }, throttle: null, - notifyWhen: 'onActionGroupChange', + notify_when: 'onActionGroupChange', actions: [ { id: defaultAction.id, @@ -243,7 +243,7 @@ export default function createNotifyWhenTests({ getService }: FtrProviderContext }) ) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const events = await retry.try(async () => { return await getEventLog({ diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/rule_types.ts similarity index 59% rename from x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/alerting/rule_types.ts index 38e38824ca9d7..3d3cec4c30252 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/list_alert_types.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/rule_types.ts @@ -14,42 +14,42 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function listAlertTypes({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('list_alert_types', () => { + describe('rule_types', () => { it('should return 200 with list of alert types', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule_types` ); expect(response.status).to.eql(200); - const { authorizedConsumers, ...fixtureAlertType } = response.body.find( + const { authorized_consumers: authorizedConsumers, ...fixtureAlertType } = response.body.find( (alertType: any) => alertType.id === 'test.noop' ); expect(fixtureAlertType).to.eql({ - actionGroups: [ + action_groups: [ { id: 'default', name: 'Default' }, { id: 'recovered', name: 'Recovered' }, ], - defaultActionGroupId: 'default', + default_action_group_id: 'default', id: 'test.noop', name: 'Test: Noop', - actionVariables: { + action_variables: { state: [], params: [], context: [], }, - recoveryActionGroup: { + recovery_action_group: { id: 'recovered', name: 'Recovered', }, producer: 'alertsFixture', - minimumLicenseRequired: 'basic', - enabledInLicense: true, + minimum_license_required: 'basic', + enabled_in_license: true, }); expect(Object.keys(authorizedConsumers)).to.contain('alertsFixture'); }); it('should return actionVariables with both context and state', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule_types` ); expect(response.status).to.eql(200); @@ -57,7 +57,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { (alertType: any) => alertType.id === 'test.always-firing' ); - expect(fixtureAlertType.actionVariables).to.eql({ + expect(fixtureAlertType.action_variables).to.eql({ state: [{ name: 'instanceStateValue', description: 'the instance state value' }], params: [{ name: 'instanceParamsValue', description: 'the instance params value' }], context: [{ name: 'instanceContextValue', description: 'the instance context value' }], @@ -66,7 +66,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { it('should return actionVariables with just context', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule_types` ); expect(response.status).to.eql(200); @@ -74,7 +74,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { (alertType: any) => alertType.id === 'test.onlyContextVariables' ); - expect(fixtureAlertType.actionVariables).to.eql({ + expect(fixtureAlertType.action_variables).to.eql({ state: [], params: [], context: [{ name: 'aContextVariable', description: 'this is a context variable' }], @@ -83,7 +83,7 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { it('should return actionVariables with just state', async () => { const response = await supertest.get( - `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + `${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule_types` ); expect(response.status).to.eql(200); @@ -91,11 +91,45 @@ export default function listAlertTypes({ getService }: FtrProviderContext) { (alertType: any) => alertType.id === 'test.onlyStateVariables' ); - expect(fixtureAlertType.actionVariables).to.eql({ + expect(fixtureAlertType.action_variables).to.eql({ state: [{ name: 'aStateVariable', description: 'this is a state variable' }], context: [], params: [], }); }); + + describe('legacy', () => { + it('should return 200 with list of alert types', async () => { + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/list_alert_types` + ); + expect(response.status).to.eql(200); + const { authorizedConsumers, ...fixtureAlertType } = response.body.find( + (alertType: any) => alertType.id === 'test.noop' + ); + expect(fixtureAlertType).to.eql({ + actionGroups: [ + { id: 'default', name: 'Default' }, + { id: 'recovered', name: 'Recovered' }, + ], + defaultActionGroupId: 'default', + id: 'test.noop', + name: 'Test: Noop', + actionVariables: { + state: [], + params: [], + context: [], + }, + recoveryActionGroup: { + id: 'recovered', + name: 'Recovered', + }, + producer: 'alertsFixture', + minimumLicenseRequired: 'basic', + enabledInLicense: true, + }); + expect(Object.keys(authorizedConsumers)).to.contain('alertsFixture'); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts index 13b021474e01e..2fffa9189e0ad 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_all.ts @@ -28,20 +28,20 @@ export default function createUnmuteTests({ getService }: FtrProviderContext) { it('should handle unmute alert request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.muteAll(createdAlert.id); await alertUtils.unmuteAll(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); - expect(updatedAlert.muteAll).to.eql(false); + expect(updatedAlert.mute_all).to.eql(false); // Ensure AAD isn't broken await checkAAD({ @@ -51,5 +51,39 @@ export default function createUnmuteTests({ getService }: FtrProviderContext) { id: createdAlert.id, }); }); + + describe('legacy', () => { + it('should handle unmute alert request appropriately', async () => { + const { body: createdAlert } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: false })) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_mute_all`) + .set('kbn-xsrf', 'foo') + .expect(204); + await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_unmute_all`) + .set('kbn-xsrf', 'foo') + .expect(204); + + const { body: updatedAlert } = await supertestWithoutAuth + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .expect(200); + expect(updatedAlert.mute_all).to.eql(false); + + // Ensure AAD isn't broken + await checkAAD({ + supertest: supertestWithoutAuth, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts index 5cba2ab2305de..e0c42136628d3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/unmute_instance.ts @@ -28,20 +28,20 @@ export default function createUnmuteInstanceTests({ getService }: FtrProviderCon it('should handle unmute alert instance request appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData({ enabled: false })) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.muteInstance(createdAlert.id, '1'); await alertUtils.unmuteInstance(createdAlert.id, '1'); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); - expect(updatedAlert.mutedInstanceIds).to.eql([]); + expect(updatedAlert.muted_alert_ids).to.eql([]); // Ensure AAD isn't broken await checkAAD({ @@ -51,5 +51,47 @@ export default function createUnmuteInstanceTests({ getService }: FtrProviderCon id: createdAlert.id, }); }); + + describe('legacy', () => { + it('should handle unmute alert instance request appropriately', async () => { + const { body: createdAlert } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData({ enabled: false })) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${ + createdAlert.id + }/alert_instance/1/_mute` + ) + .set('kbn-xsrf', 'foo') + .expect(204); + await supertestWithoutAuth + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${ + createdAlert.id + }/alert_instance/1/_unmute` + ) + .set('kbn-xsrf', 'foo') + .expect(204); + + const { body: updatedAlert } = await supertestWithoutAuth + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .expect(200); + expect(updatedAlert.muted_alert_ids).to.eql([]); + + // Ensure AAD isn't broken + await checkAAD({ + supertest: supertestWithoutAuth, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts index d97352966504a..318da3e114097 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts @@ -21,11 +21,11 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it('should handle update alert request appropriately', async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); const updatedData = { name: 'bcd', @@ -36,9 +36,10 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, actions: [], throttle: '1m', + notify_when: 'onThrottleInterval', }; const response = await supertest - .put(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .send(updatedData) .expect(200); @@ -47,24 +48,24 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { ...updatedData, id: createdAlert.id, tags: ['bar'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alertsFixture', - createdBy: null, + created_by: null, enabled: true, - updatedBy: null, - apiKeyOwner: null, - muteAll: false, - mutedInstanceIds: [], - notifyWhen: 'onThrottleInterval', - scheduledTaskId: createdAlert.scheduledTaskId, - createdAt: response.body.createdAt, - updatedAt: response.body.updatedAt, - executionStatus: response.body.executionStatus, + updated_by: null, + api_key_owner: null, + mute_all: false, + muted_alert_ids: [], + notify_when: 'onThrottleInterval', + scheduled_task_id: createdAlert.scheduled_task_id, + created_at: response.body.created_at, + updated_at: response.body.updated_at, + execution_status: response.body.execution_status, }); - expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); - expect(Date.parse(response.body.updatedAt)).to.be.greaterThan( - Date.parse(response.body.createdAt) + expect(Date.parse(response.body.created_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan(0); + expect(Date.parse(response.body.updated_at)).to.be.greaterThan( + Date.parse(response.body.created_at) ); // Ensure AAD isn't broken @@ -78,14 +79,14 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { it(`shouldn't update alert from another space`, async () => { const { body: createdAlert } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await supertest - .put(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert/${createdAlert.id}`) + .put(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .send({ name: 'bcd', @@ -96,6 +97,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { schedule: { interval: '12s' }, actions: [], throttle: '1m', + notify_when: 'onThrottleInterval', }) .expect(404, { statusCode: 404, @@ -103,5 +105,65 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { message: `Saved object [alert/${createdAlert.id}] not found`, }); }); + + describe('legacy', () => { + it('should handle update alert request appropriately', async () => { + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + const updatedData = { + name: 'bcd', + tags: ['bar'], + params: { + foo: true, + }, + schedule: { interval: '12s' }, + actions: [], + throttle: '1m', + notifyWhen: 'onThrottleInterval', + }; + const response = await supertest + .put(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .send(updatedData) + .expect(200); + + expect(response.body).to.eql({ + ...updatedData, + id: createdAlert.id, + tags: ['bar'], + alertTypeId: 'test.noop', + consumer: 'alertsFixture', + createdBy: null, + enabled: true, + updatedBy: null, + apiKeyOwner: null, + muteAll: false, + mutedInstanceIds: [], + notifyWhen: 'onThrottleInterval', + scheduledTaskId: createdAlert.scheduled_task_id, + createdAt: response.body.createdAt, + updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, + }); + expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); + expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); + expect(Date.parse(response.body.updatedAt)).to.be.greaterThan( + Date.parse(response.body.createdAt) + ); + + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts index 32ce38d141772..78ceadec44a9a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update_api_key.ts @@ -32,19 +32,19 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it('should handle update alert api key appropriately', async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.updateApiKey(createdAlert.id); const { body: updatedAlert } = await supertestWithoutAuth - .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}`) + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) .set('kbn-xsrf', 'foo') .expect(200); - expect(updatedAlert.apiKeyOwner).to.eql(null); + expect(updatedAlert.api_key_owner).to.eql(null); // Ensure AAD isn't broken await checkAAD({ @@ -57,11 +57,11 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte it(`shouldn't update alert api key from another space`, async () => { const { body: createdAlert } = await supertestWithoutAuth - .post(`${getUrlPrefix(Spaces.other.id)}/api/alerts/alert`) + .post(`${getUrlPrefix(Spaces.other.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); - objectRemover.add(Spaces.other.id, createdAlert.id, 'alert', 'alerts'); + objectRemover.add(Spaces.other.id, createdAlert.id, 'rule', 'alerting'); await alertUtils.getUpdateApiKeyRequest(createdAlert.id).expect(404, { statusCode: 404, @@ -69,5 +69,37 @@ export default function createUpdateApiKeyTests({ getService }: FtrProviderConte message: `Saved object [alert/${createdAlert.id}] not found`, }); }); + + describe('legacy', () => { + it('should handle update alert api key appropriately', async () => { + const { body: createdAlert } = await supertestWithoutAuth + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAlert.id, 'rule', 'alerting'); + + await supertestWithoutAuth + .post( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${createdAlert.id}/_update_api_key` + ) + .set('kbn-xsrf', 'foo') + .expect(204); + + const { body: updatedAlert } = await supertestWithoutAuth + .get(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule/${createdAlert.id}`) + .set('kbn-xsrf', 'foo') + .expect(200); + expect(updatedAlert.api_key_owner).to.eql(null); + + // Ensure AAD isn't broken + await checkAAD({ + supertest: supertestWithoutAuth, + spaceId: Spaces.space1.id, + type: 'alert', + id: createdAlert.id, + }); + }); + }); }); } diff --git a/x-pack/test/api_integration/apis/file_upload/has_import_permission.ts b/x-pack/test/api_integration/apis/file_upload/has_import_permission.ts new file mode 100644 index 0000000000000..3977c4f0a06ad --- /dev/null +++ b/x-pack/test/api_integration/apis/file_upload/has_import_permission.ts @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getService }: FtrProviderContext) => { + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const security = getService('security'); + + const IMPORTER_ROLE_NAME = 'importer'; + const IMPORTER_USER_NAME = 'importer'; + const IMPORT_USER_PASSWORD = `${IMPORTER_USER_NAME}-password`; + const INDEX_NAME = 'myNewIndex'; + + describe('GET /internal/file_upload/has_import_permission', () => { + it('should return true when user has all permissions', async () => { + try { + await security.role.create(IMPORTER_ROLE_NAME, { + elasticsearch: { + cluster: ['manage_pipeline'], + indices: [ + { + names: [INDEX_NAME], + privileges: ['create', 'create_index'], + }, + ], + }, + kibana: [ + { + feature: { + indexPatterns: ['all'], + }, + spaces: ['*'], + }, + ], + }); + + await security.user.create(IMPORTER_USER_NAME, { + password: IMPORT_USER_PASSWORD, + roles: [IMPORTER_ROLE_NAME], + }); + + const resp = await supertestWithoutAuth + .get( + `/internal/file_upload/has_import_permission\ +?checkCreateIndexPattern=true\ +&checkHasManagePipeline=true\ +&indexName=${INDEX_NAME}` + ) + .auth(IMPORTER_USER_NAME, IMPORT_USER_PASSWORD) + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(resp.body.hasImportPermission).to.be(true); + } finally { + await security.role.delete(IMPORTER_ROLE_NAME); + await security.user.delete(IMPORTER_USER_NAME); + } + }); + + it('should return false when user can not create index pattern when checkCreateIndexPattern=true', async () => { + try { + await security.role.create(IMPORTER_ROLE_NAME, {}); + + await security.user.create(IMPORTER_USER_NAME, { + password: IMPORT_USER_PASSWORD, + roles: [IMPORTER_ROLE_NAME], + }); + + const resp = await supertestWithoutAuth + .get( + `/internal/file_upload/has_import_permission\ +?checkCreateIndexPattern=true\ +&checkHasManagePipeline=false` + ) + .auth(IMPORTER_USER_NAME, IMPORT_USER_PASSWORD) + .set('kbn-xsrf', 'kibana') + .send() + .expect(200); + + expect(resp.body.hasImportPermission).to.be(false); + } finally { + await security.role.delete(IMPORTER_ROLE_NAME); + await security.user.delete(IMPORTER_USER_NAME); + } + }); + + it('should return false when user can not create pipeline when checkHasManagePipeline=true', async () => { + try { + await security.role.create(IMPORTER_ROLE_NAME, {}); + + await security.user.create(IMPORTER_USER_NAME, { + password: IMPORT_USER_PASSWORD, + roles: [IMPORTER_ROLE_NAME], + }); + + const resp = await supertestWithoutAuth + .get( + `/internal/file_upload/has_import_permission\ +?checkCreateIndexPattern=false\ +&checkHasManagePipeline=true` + ) + .auth(IMPORTER_USER_NAME, IMPORT_USER_PASSWORD) + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(resp.body.hasImportPermission).to.be(false); + } finally { + await security.role.delete(IMPORTER_ROLE_NAME); + await security.user.delete(IMPORTER_USER_NAME); + } + }); + + it('should return false when user does not have index permissions', async () => { + try { + await security.role.create(IMPORTER_ROLE_NAME, {}); + + await security.user.create(IMPORTER_USER_NAME, { + password: IMPORT_USER_PASSWORD, + roles: [IMPORTER_ROLE_NAME], + }); + + const resp = await supertestWithoutAuth + .get( + `/internal/file_upload/has_import_permission\ +?checkCreateIndexPattern=false\ +&checkHasManagePipeline=false\ +&indexName=${INDEX_NAME}` + ) + .auth(IMPORTER_USER_NAME, IMPORT_USER_PASSWORD) + .set('kbn-xsrf', 'kibana') + .expect(200); + + expect(resp.body.hasImportPermission).to.be(false); + } finally { + await security.role.delete(IMPORTER_ROLE_NAME); + await security.user.delete(IMPORTER_USER_NAME); + } + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/file_upload/index.ts b/x-pack/test/api_integration/apis/file_upload/index.ts new file mode 100644 index 0000000000000..cf0159997fa11 --- /dev/null +++ b/x-pack/test/api_integration/apis/file_upload/index.ts @@ -0,0 +1,14 @@ +/* + * 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 function ({ loadTestFile }: FtrProviderContext) { + describe('File upload', function () { + loadTestFile(require.resolve('./has_import_permission')); + }); +} diff --git a/x-pack/test/api_integration/apis/index.ts b/x-pack/test/api_integration/apis/index.ts index 26c3a2e251b1e..e032844470177 100644 --- a/x-pack/test/api_integration/apis/index.ts +++ b/x-pack/test/api_integration/apis/index.ts @@ -36,5 +36,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./upgrade_assistant')); loadTestFile(require.resolve('./searchprofiler')); loadTestFile(require.resolve('./painless_lab')); + loadTestFile(require.resolve('./file_upload')); }); } diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts index e0c390e325ea0..63d4e2c4a874f 100644 --- a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts +++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_histograms.ts @@ -115,7 +115,7 @@ export default ({ getService }: FtrProviderContext) => { ); expect(body.error).to.eql(errorTestData.expected.responseBody.error); - expect(body.message).to.eql(errorTestData.expected.responseBody.message); + expect(body.message).to.contain(errorTestData.expected.responseBody.message); }); }); }; diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts index 5def2a4950147..6e7231065c377 100644 --- a/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts +++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_field_stats.ts @@ -227,7 +227,7 @@ export default ({ getService }: FtrProviderContext) => { ); expect(body.error).to.eql(errorTestData.expected.responseBody.error); - expect(body.message).to.eql(errorTestData.expected.responseBody.message); + expect(body.message).to.contain(errorTestData.expected.responseBody.message); }); }); }; diff --git a/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts index 91ef8c19a5a1d..601a4a04eb0c6 100644 --- a/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts +++ b/x-pack/test/api_integration/apis/ml/data_visualizer/get_overall_stats.ts @@ -142,7 +142,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body).to.eql(testData.expected.responseBody); } else { expect(body.error).to.eql(testData.expected.responseBody.error); - expect(body.message).to.eql(testData.expected.responseBody.message); + expect(body.message).to.contain(testData.expected.responseBody.message); } }); } diff --git a/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts b/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts index 158290d29f346..13941f0c1e145 100644 --- a/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts +++ b/x-pack/test/api_integration/apis/ml/fields_service/field_cardinality.ts @@ -103,7 +103,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body).to.eql(testData.expected.responseBody); } else { expect(body.error).to.eql(testData.expected.responseBody.error); - expect(body.message).to.eql(testData.expected.responseBody.message); + expect(body.message).to.contain(testData.expected.responseBody.message); } }); } diff --git a/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts b/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts index dd30dced42c63..c95cf4e4f9ce4 100644 --- a/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts +++ b/x-pack/test/api_integration/apis/ml/fields_service/time_field_range.ts @@ -107,7 +107,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body).to.eql(testData.expected.responseBody); } else { expect(body.error).to.eql(testData.expected.responseBody.error); - expect(body.message).to.eql(testData.expected.responseBody.message); + expect(body.message).to.contain(testData.expected.responseBody.message); } }); } diff --git a/x-pack/test/api_integration/apis/transform/transforms_preview.ts b/x-pack/test/api_integration/apis/transform/transforms_preview.ts index 670dc936b6d1e..1acac929ef359 100644 --- a/x-pack/test/api_integration/apis/transform/transforms_preview.ts +++ b/x-pack/test/api_integration/apis/transform/transforms_preview.ts @@ -78,7 +78,7 @@ export default ({ getService }: FtrProviderContext) => { }) .expect(400); - expect(body.message).to.eql( + expect(body.message).to.contain( '[parsing_exception] Unknown aggregation type [value_countt] did you mean [value_count]?, with line=1 & col=43' ); }); diff --git a/x-pack/test/functional/apps/maps/import_geojson/add_layer_import_panel.js b/x-pack/test/functional/apps/maps/import_geojson/add_layer_import_panel.js index b40f9a4bc233e..5af0a2c6d1edb 100644 --- a/x-pack/test/functional/apps/maps/import_geojson/add_layer_import_panel.js +++ b/x-pack/test/functional/apps/maps/import_geojson/add_layer_import_panel.js @@ -17,7 +17,11 @@ export default function ({ getPageObjects, getService }) { describe('GeoJSON import layer panel', () => { before(async () => { - await security.testUser.setRoles(['global_maps_all', 'geoall_data_writer']); + await security.testUser.setRoles([ + 'global_maps_all', + 'geoall_data_writer', + 'global_index_pattern_management_all', + ]); await PageObjects.maps.openNewMap(); }); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts index d73488fab2373..661b452855a86 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts @@ -20,14 +20,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function getAlertsByName(name: string) { const { body: { data: alerts }, - } = await supertest.get(`/api/alerts/_find?search=${name}&search_fields=name`).expect(200); + } = await supertest + .get(`/api/alerting/rules/_find?search=${name}&search_fields=name`) + .expect(200); return alerts; } async function deleteAlerts(alertIds: string[]) { alertIds.forEach(async (alertId: string) => { - await supertest.delete(`/api/alerts/alert/${alertId}`).set('kbn-xsrf', 'foo').expect(204, ''); + await supertest + .delete(`/api/alerting/rule/${alertId}`) + .set('kbn-xsrf', 'foo') + .expect(204, ''); }); } diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index e73e1fd8c3713..550e6ca455b22 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -20,7 +20,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createAlertManualCleanup(overwrites: Record = {}) { const { body: createdAlert } = await supertest - .post(`/api/alerts/alert`) + .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData(overwrites)) .expect(200); @@ -29,7 +29,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createFailingAlert() { return await createAlert({ - alertTypeId: 'test.failing', + rule_type_id: 'test.failing', schedule: { interval: '30s' }, }); } @@ -445,7 +445,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { actions: [ { id: action.id, - actionTypeId: '.slack', group: 'default', params: { level: 'info', message: 'gfghfhg' }, }, diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 5c4566121d478..97f8b3f61dc89 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -42,7 +42,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createAlert(overwrites: Record = {}) { const { body: createdAlert } = await supertest - .post(`/api/alerts/alert`) + .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData(overwrites)) .expect(200); @@ -52,11 +52,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createAlwaysFiringAlert(overwrites: Record = {}) { const { body: createdAlert } = await supertest - .post(`/api/alerts/alert`) + .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send( getTestAlertData({ - alertTypeId: 'test.always-firing', + rule_type_id: 'test.always-firing', ...overwrites, }) ) @@ -93,14 +93,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function getAlertInstanceSummary(alertId: string) { const { body: summary } = await supertest - .get(`/api/alerts/alert/${alertId}/_instance_summary`) + .get(`/internal/alerting/rule/${alertId}/_alert_summary`) .expect(200); return summary; } async function muteAlertInstance(alertId: string, alertInstanceId: string) { const { body: response } = await supertest - .post(`/api/alerts/alert/${alertId}/alert_instance/${alertInstanceId}/_mute`) + .post(`/api/alerting/rule/${alertId}/alert/${alertInstanceId}/_mute`) .set('kbn-xsrf', 'foo') .expect(204); @@ -221,7 +221,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await createAlwaysFiringAlert({ name: alertName, - alertTypeId: '.index-threshold', + rule_type_id: '.index-threshold', params: { aggType: 'count', termSize: 5, @@ -526,7 +526,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // await first run to complete so we have an initial state await retry.try(async () => { - const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); + const { alerts: alertInstances } = await getAlertInstanceSummary(alert.id); expect(Object.keys(alertInstances).length).to.eql(instances.length); }); }); @@ -552,12 +552,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const summary = await getAlertInstanceSummary(alert.id); const dateOnAllInstancesFromApiResponse: Record = mapValues( - summary.instances, + summary.alerts, (instance) => instance.activeStartDate ); const actionGroupNameOnAllInstancesFromApiResponse = mapValues( - summary.instances, + summary.alerts, (instance) => { const name = actionGroupNameFromId(instance.actionGroupId); return name ? ` (${name})` : ''; @@ -724,7 +724,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // await first run to complete so we have an initial state await retry.try(async () => { - const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); + const { alerts: alertInstances } = await getAlertInstanceSummary(alert.id); expect(Object.keys(alertInstances).length).to.eql(instances.length); }); @@ -749,7 +749,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Verify content await testSubjects.existOrFail('alertInstancesList'); - const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); + const { alerts: alertInstances } = await getAlertInstanceSummary(alert.id); const items = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect(items.length).to.eql(PAGE_SIZE); @@ -762,7 +762,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Verify content await testSubjects.existOrFail('alertInstancesList'); - const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); + const { alerts: alertInstances } = await getAlertInstanceSummary(alert.id); await pageObjects.alertDetailsUI.clickPaginationNextPage(); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts index 4aeadf5f1ae8a..8ebb720930364 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts @@ -91,7 +91,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { objectRemover.add(createdAction.id, 'action', 'actions'); const { body: createdAlert } = await supertest - .post(`/api/alerts/alert`) + .post(`/api/alerting/rule`) .set('kbn-xsrf', 'foo') .send(getTestAlertData()) .expect(200); diff --git a/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts b/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts index 11ccd15571259..8dc8a5e06645c 100644 --- a/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts +++ b/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts @@ -16,11 +16,11 @@ export function getTestAlertData(overwrites = {}) { enabled: true, name: generateUniqueKey(), tags: ['foo', 'bar'], - alertTypeId: 'test.noop', + rule_type_id: 'test.noop', consumer: 'alerts', schedule: { interval: '1m' }, throttle: '1m', - notifyWhen: 'onThrottleInterval', + notify_when: 'onThrottleInterval', actions: [], params: {}, ...overwrites, diff --git a/yarn.lock b/yarn.lock index ce504fd9e9686..0bbfe98f5d1d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2618,6 +2618,10 @@ version "0.0.0" uid "" +"@kbn/cli-dev-mode@link:packages/kbn-cli-dev-mode": + version "0.0.0" + uid "" + "@kbn/config-schema@link:packages/kbn-config-schema": version "0.0.0" uid "" @@ -2626,6 +2630,10 @@ version "0.0.0" uid "" +"@kbn/crypto@link:packages/kbn-crypto": + version "0.0.0" + uid "" + "@kbn/dev-utils@link:packages/kbn-dev-utils": version "0.0.0" uid "" @@ -2690,6 +2698,10 @@ version "0.0.0" uid "" +"@kbn/server-http-tools@link:packages/kbn-server-http-tools": + version "0.0.0" + uid "" + "@kbn/std@link:packages/kbn-std": version "0.0.0" uid ""